UNPKG

scandit-sdk

Version:

Scandit Barcode Scanner SDK for the Web

719 lines 34.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CameraAccess = void 0; var tslib_1 = require("tslib"); var cameraManager_1 = require("./barcodePicker/cameraManager"); var browserCompatibility_1 = require("./browserCompatibility"); var browserHelper_1 = require("./browserHelper"); var camera_1 = require("./camera"); var customError_1 = require("./customError"); var logger_1 = require("./logger"); var unsupportedBrowserError_1 = require("./unsupportedBrowserError"); /** * A helper object to interact with cameras. */ var CameraAccess; (function (CameraAccess) { /** * @hidden * * Standard error names mapping. */ var standardErrorNamesMapping = new Map([ ["DeviceCaptureError", "AbortError"], ["NotSupportedError", "AbortError"], ["ScreenCaptureError", "AbortError"], ["TabCaptureError", "AbortError"], ["TypeError", "AbortError"], ["InvalidStateError", "NotAllowedError"], ["MediaDeviceFailedDueToShutdown", "NotAllowedError"], ["MediaDeviceKillSwitchOn", "NotAllowedError"], ["PermissionDeniedError", "NotAllowedError"], ["PermissionDismissedError", "NotAllowedError"], ["DevicesNotFoundError", "NotFoundError"], ["SourceUnavailableError", "NotReadableError"], ["TrackStartError", "NotReadableError"], ["ConstraintNotSatisfiedError", "OverconstrainedError"], ]); /** * @hidden * * Handle localized camera labels. Supported languages: * English, German, French, Spanish (spain), Portuguese (brasil), Portuguese (portugal), Italian, * Chinese (simplified), Chinese (traditional), Japanese, Russian, Turkish, Dutch, Arabic, Thai, Swedish, * Danish, Vietnamese, Norwegian, Polish, Finnish, Indonesian, Hebrew, Greek, Romanian, Hungarian, Czech, * Catalan, Slovak, Ukraininan, Croatian, Malay, Hindi. */ var backCameraKeywords = [ "rear", "back", "rück", "arrière", "trasera", "trás", "traseira", "posteriore", "后面", "後面", "背面", "后置", "後置", "背置", "задней", "الخلفية", "후", "arka", "achterzijde", "หลัง", "baksidan", "bagside", "sau", "bak", "tylny", "takakamera", "belakang", "אחורית", "πίσω", "spate", "hátsó", "zadní", "darrere", "zadná", "задня", "stražnja", "belakang", "बैक", ]; /** * @hidden * * The (cached) list of available video devices, updated when [[getCameras]] is called for the first time and after * subsequent calls with the *refreshDevices* parameter enabled. The contained devices' order never changes, howver * their deviceIds could change when they are retrieved again after a camera access and stop in some situations. */ var availableVideoDevices; /** * @hidden * * Whether the currently cached available devices are out of date because of a `devicechange` event. */ var outdatedDevices = false; /** * @hidden * * Overrides for main camera for a given camera type on a desktop/laptop device, set when accessing an initial camera. */ CameraAccess.mainCameraForTypeOverridesOnDesktop = new Map(); /** * @hidden * * To be accessed directly only for tests. * * The mapping from deviceIds to camera objects. */ CameraAccess.deviceIdToCameraObjects = new Map(); /** * @hidden * * To be accessed directly only for tests. * * The list of inaccessible deviceIds. */ CameraAccess.inaccessibleDeviceIds = new Set(); /** * @hidden * * Listen to `devicechange` events. */ function deviceChangeListener() { outdatedDevices = true; } /** * @hidden * * @param label The camera label. * @returns Whether the label mentions the camera being a back-facing one. */ function isBackCameraLabel(label) { var lowercaseLabel = label.toLowerCase(); return backCameraKeywords.some(function (keyword) { return lowercaseLabel.includes(keyword); }); } /** * @hidden * * Map non-standard error names to standard ones. * * @param error The error object. */ function mapNonStandardErrorName(error) { var _a; var name; if (error.message === "Invalid constraint") { name = "OverconstrainedError"; } else { name = (_a = standardErrorNamesMapping.get(error.name)) !== null && _a !== void 0 ? _a : error.name; } Object.defineProperty(error, "name", { value: name, }); } /** * @hidden * * Get the main camera for the given camera type. * * @param cameras The array of available [[Camera]] objects. * @param cameraType The wanted camera type. * @returns The main camera matching the wanted camera type. */ function getMainCameraForType(cameras, cameraType) { var mainCameraForType; if (browserHelper_1.BrowserHelper.isDesktopDevice()) { // When the device is a desktop/laptop, the overridden camera for the given type or, if not present, the first // camera of the given type is the main one. if (CameraAccess.mainCameraForTypeOverridesOnDesktop.has(cameraType)) { mainCameraForType = CameraAccess.mainCameraForTypeOverridesOnDesktop.get(cameraType); } else { // Note that if the device is a desktop/laptop, with no labels all cameras are assumed to be front ones, // so this will return the first camera as the main front one and none for the back one. mainCameraForType = cameras.filter(function (camera) { return camera.cameraType === cameraType; })[0]; } } else { var allHaveBlankLabel = cameras.every(function (camera) { return camera.label === ""; }); var allHaveNonEmptyLabel = cameras.every(function (camera) { return camera.label !== ""; }); var someHaveLabel = cameras.length > 1 && !allHaveBlankLabel && !allHaveNonEmptyLabel; if (allHaveBlankLabel) { // When no camera label is available cameras are already in front to back order, assume main front camera is the // first one and main back camera is the last one. mainCameraForType = cameras[cameraType === camera_1.Camera.Type.FRONT ? 0 : cameras.length - 1]; } else if (someHaveLabel) { // When only a few cameras have labels, we may be in a webview where only labels from accessed stream are // available. var cameraOfType = cameras.filter(function (camera) { return camera.cameraType === cameraType; }); if (cameraOfType.length === 1) { mainCameraForType = cameraOfType[0]; } else if (cameraOfType.length > 1) { // Assume main front camera is the first one and main back camera is the last one. mainCameraForType = cameraOfType[cameraType === camera_1.Camera.Type.FRONT ? 0 : cameraOfType.length - 1]; } } else { mainCameraForType = cameras .filter(function (camera) { return camera.cameraType === cameraType; }) // sort so that camera list looks like ['camera1 0', 'camera1 1', 'camera2 0', 'camera2 1'] .sort(function (camera1, camera2) { return camera1.label.localeCompare(camera2.label); })[0]; } } return mainCameraForType; } CameraAccess.getMainCameraForType = getMainCameraForType; /** * @hidden * * Sort the given cameras in order of priority of access based on the given camera type. * * @param cameras The array of available [[Camera]] objects. * @param cameraType The preferred camera type. * @returns The sorted cameras. */ function sortCamerasForCameraType(cameras, cameraType) { function prioritizeMainCameraOverride(prioritizedCameras, currentCameraType) { var mainCameraOverride = CameraAccess.mainCameraForTypeOverridesOnDesktop.get(currentCameraType); if (mainCameraOverride != null && prioritizedCameras.includes(mainCameraOverride)) { prioritizedCameras = prioritizedCameras.filter(function (camera) { return camera !== mainCameraOverride; }); prioritizedCameras.unshift(mainCameraOverride); } return prioritizedCameras; } var frontCameras = cameras.filter(function (camera) { return camera.cameraType === camera_1.Camera.Type.FRONT; }); var backCameras = cameras.filter(function (camera) { return camera.cameraType === camera_1.Camera.Type.BACK; }); if (browserHelper_1.BrowserHelper.isDesktopDevice()) { // When the device is a desktop/laptop, the cameras for each type are already ordered, we move the overrides // first if present and change front / back group order if needed. frontCameras = prioritizeMainCameraOverride(frontCameras, camera_1.Camera.Type.FRONT); backCameras = prioritizeMainCameraOverride(backCameras, camera_1.Camera.Type.BACK); } else if (cameras.every(function (camera) { return camera.label === ""; })) { // When no camera label is available cameras are already in front to back order, we assume front cameras are // ordered and back cameras are in reversed order (try to access last first), and we change front / back group // order if needed. backCameras.reverse(); } else { frontCameras.sort(function (camera1, camera2) { return camera1.label.localeCompare(camera2.label); }); backCameras.sort(function (camera1, camera2) { return camera1.label.localeCompare(camera2.label); }); } return cameraType === camera_1.Camera.Type.FRONT ? tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(frontCameras), false), tslib_1.__read(backCameras), false) : tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(backCameras), false), tslib_1.__read(frontCameras), false); } CameraAccess.sortCamerasForCameraType = sortCamerasForCameraType; /** * @hidden * * Adjusts the camera's information based on the given currently active video stream. * * @param mediaStream The currently active `MediaStream` object. * @param camera The currently active [[Camera]] object associated with the video stream. */ function adjustCameraFromMediaStream(mediaStream, camera) { var videoTracks = mediaStream.getVideoTracks(); if (videoTracks.length !== 0) { var mediaStreamTrack = videoTracks[0]; var mediaTrackSettings = void 0; if (typeof mediaStreamTrack.getSettings === "function") { mediaTrackSettings = mediaStreamTrack.getSettings(); if ((mediaTrackSettings === null || mediaTrackSettings === void 0 ? void 0 : mediaTrackSettings.facingMode) != null && mediaTrackSettings.facingMode.length > 0) { camera.cameraType = mediaTrackSettings.facingMode === "environment" ? camera_1.Camera.Type.BACK : camera_1.Camera.Type.FRONT; } } if (mediaStreamTrack.label != null && mediaStreamTrack.label.length > 0) { camera.label = mediaStreamTrack.label; } } } CameraAccess.adjustCameraFromMediaStream = adjustCameraFromMediaStream; /** * @hidden * * @param devices The list of available devices. * @returns The extracted list of accessible camera objects initialized from the given devices. */ function extractAccessibleCamerasFromDevices(devices) { function createCamera(videoDevice, index, videoDevices) { var _a; if (CameraAccess.deviceIdToCameraObjects.has(videoDevice.deviceId)) { return CameraAccess.deviceIdToCameraObjects.get(videoDevice.deviceId); } var label = (_a = videoDevice.label) !== null && _a !== void 0 ? _a : ""; var cameraType; if (!browserHelper_1.BrowserHelper.isDesktopDevice() && videoDevices.every(function (device) { return device.label === "" && !CameraAccess.deviceIdToCameraObjects.has(device.deviceId); })) { // When the device is not a desktop/laptop and no camera label is available, assume the camera is a front one // if it's the only one or comes in the first half of the list of cameras (if an odd number of cameras is // available, it's more likely to have more back than front ones). cameraType = videoDevices.length === 1 || index + 1 <= Math.floor(videoDevices.length / 2) ? camera_1.Camera.Type.FRONT : camera_1.Camera.Type.BACK; } else { // Note that if the device is a desktop/laptop, unless the label specifies a back camera, a front one is assumed cameraType = isBackCameraLabel(label) ? camera_1.Camera.Type.BACK : camera_1.Camera.Type.FRONT; } return { deviceId: videoDevice.deviceId, label: label, cameraType: cameraType, }; } var cameras = devices .map(createCamera) .map(function (camera) { // If it's the initial camera, do nothing if (camera.deviceId !== "") { CameraAccess.deviceIdToCameraObjects.set(camera.deviceId, camera); } return camera; }) .filter(function (camera) { // Ignore infrared cameras as they often fail to be accessed and are not useful in any case return !/\b(?:ir|infrared)\b/i.test(camera.label); }) .filter(function (camera) { return !CameraAccess.inaccessibleDeviceIds.has(camera.deviceId); }); if (!browserHelper_1.BrowserHelper.isDesktopDevice() && cameras.length > 1 && !cameras.some(function (camera) { return camera.cameraType === camera_1.Camera.Type.BACK; })) { // When the device is not a desktop/laptop check if cameras are labeled with resolution information, if that's the // case, take the higher - resolution one, otherwise pick the last camera (it's not true that the primary camera // is first in most scenarios) and mark it as the back one. var backCameraIndex = cameras.length - 1; var cameraResolutions = cameras.map(function (camera) { var match = camera.label.match(/\b([0-9]+)MP?\b/i); if (match != null) { return parseInt(match[1], 10); } return NaN; }); if (!cameraResolutions.some(function (cameraResolution) { return isNaN(cameraResolution); })) { backCameraIndex = cameraResolutions.lastIndexOf(Math.max.apply(Math, tslib_1.__spreadArray([], tslib_1.__read(cameraResolutions), false))); } cameras[backCameraIndex].cameraType = camera_1.Camera.Type.BACK; } return cameras; } /** * @hidden * * @returns The stream, if necessary, accessed to provide access to complete device information */ function getStreamForDeviceAccessPermission() { return tslib_1.__awaiter(this, void 0, void 0, function () { var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: if (!(availableVideoDevices != null && availableVideoDevices.length > 0 && availableVideoDevices.every(function (device) { return device.label === "" && !CameraAccess.deviceIdToCameraObjects.has(device.deviceId); }))) return [3 /*break*/, 4]; _b.label = 1; case 1: _b.trys.push([1, 3, , 4]); return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ video: true, audio: false, })]; case 2: return [2 /*return*/, _b.sent()]; case 3: _a = _b.sent(); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); } /** * @hidden * * Checks and adjust cameras' deviceId information and related information if a change is detected. We can rely on the * fact that devices are returned in the same order even when deviceId information changes. * * @param oldAvailableDevices The old list of available devices before deviceId information was refreshed. * @param newAvailableDevices The new list of available devices after deviceId information was refreshed. */ function checkAndUpdateCameraDeviceIdInformation(oldAvailableDevices, newAvailableDevices) { if (newAvailableDevices.length > 0 && oldAvailableDevices.length === newAvailableDevices.length && !newAvailableDevices.every(function (device, index) { return oldAvailableDevices[index].deviceId === device.deviceId; })) { var deviceIdChanges_1 = {}; oldAvailableDevices.forEach(function (device, index) { var _a; var camera = CameraAccess.deviceIdToCameraObjects.get(device.deviceId); if (camera == null || camera.label !== ((_a = newAvailableDevices[index].label) !== null && _a !== void 0 ? _a : "")) { return; } var newDeviceId = newAvailableDevices[index].deviceId; deviceIdChanges_1[camera.deviceId] = newDeviceId; if (CameraAccess.inaccessibleDeviceIds.has(camera.deviceId)) { CameraAccess.inaccessibleDeviceIds.add(newDeviceId); } camera.deviceId = newDeviceId; CameraAccess.deviceIdToCameraObjects.set(newDeviceId, camera); }); logger_1.Logger.log(logger_1.Logger.Level.DEBUG, "Detected updated camera deviceId information and updated it accordingly", deviceIdChanges_1); } } /** * Get a list of cameras (if any) available on the device, a camera access permission is requested to the user * the first time this method is called if needed. * * If the browser is incompatible the returned promise is rejected with a `UnsupportedBrowserError` error. * * When refreshing available devices, if updated deviceId information is detected, cameras' deviceId are updated * accordingly. This could happen after a camera access and stop in some situations. * * @param refreshDevices Force a call to refresh available video devices even when information is already available. * @param cameraAlreadyAccessed Hint that a camera has already been accessed before, avoiding a possible initial * camera access permission request on the first call, in cases this cannot be already reliably detected. * @returns A promise resolving to the array of available [[Camera]] objects (could be empty). */ function getCameras(refreshDevices, cameraAlreadyAccessed) { if (refreshDevices === void 0) { refreshDevices = false; } if (cameraAlreadyAccessed === void 0) { cameraAlreadyAccessed = false; } return tslib_1.__awaiter(this, void 0, void 0, function () { var browserCompatibility, stream, oldAvailableDevices, error_1, cameras; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: browserCompatibility = browserHelper_1.BrowserHelper.checkBrowserCompatibility(); if (!browserCompatibility.fullSupport) { throw new unsupportedBrowserError_1.UnsupportedBrowserError(browserCompatibility); } // This will add the listeners only once in case of multiple calls: identical listeners are ignored navigator.mediaDevices.addEventListener("devicechange", deviceChangeListener); if (!(availableVideoDevices == null || refreshDevices || outdatedDevices)) return [3 /*break*/, 8]; outdatedDevices = false; stream = void 0; oldAvailableDevices = availableVideoDevices !== null && availableVideoDevices !== void 0 ? availableVideoDevices : []; availableVideoDevices = []; _a.label = 1; case 1: _a.trys.push([1, 6, 7, 8]); return [4 /*yield*/, enumerateVideoDevices()]; case 2: availableVideoDevices = _a.sent(); if (!!cameraAlreadyAccessed) return [3 /*break*/, 5]; return [4 /*yield*/, getStreamForDeviceAccessPermission()]; case 3: stream = _a.sent(); if (!(stream != null)) return [3 /*break*/, 5]; return [4 /*yield*/, enumerateVideoDevices()]; case 4: availableVideoDevices = _a.sent(); _a.label = 5; case 5: logger_1.Logger.log.apply(logger_1.Logger, tslib_1.__spreadArray([logger_1.Logger.Level.DEBUG, "Camera list (devices):"], tslib_1.__read(availableVideoDevices), false)); checkAndUpdateCameraDeviceIdInformation(oldAvailableDevices, availableVideoDevices); return [3 /*break*/, 8]; case 6: error_1 = _a.sent(); mapNonStandardErrorName(error_1); throw error_1; case 7: if (stream != null) { stream.getVideoTracks().forEach(function (track) { track.stop(); }); } return [7 /*endfinally*/]; case 8: cameras = extractAccessibleCamerasFromDevices(availableVideoDevices); logger_1.Logger.log.apply(logger_1.Logger, tslib_1.__spreadArray([logger_1.Logger.Level.DEBUG, "Camera list (cameras): "], tslib_1.__read(cameras), false)); // Return a copy of the array to allow for array mutations in other functions return [2 /*return*/, tslib_1.__spreadArray([], tslib_1.__read(cameras), false)]; } }); }); } CameraAccess.getCameras = getCameras; /** * @hidden * * Call `navigator.mediaDevices.getUserMedia` asynchronously in a `setTimeout` call. * * @param getUserMediaParams The parameters for the `navigator.mediaDevices.getUserMedia` call. * @returns A promise resolving when the camera is accessed. */ function getUserMediaDelayed(getUserMediaParams) { logger_1.Logger.log(logger_1.Logger.Level.DEBUG, "Attempt to access camera (parameters):", getUserMediaParams.video); return new Promise(function (resolve, reject) { window.setTimeout(function () { var _a; ((_a = navigator.mediaDevices.getUserMedia(getUserMediaParams)) !== null && _a !== void 0 ? _a : Promise.reject(new customError_1.CustomError({ name: "AbortError" }))) .then(resolve) .catch(reject); }, 0); }); } /** * @hidden * * Get the *getUserMedia* *video* parameters to be used given a resolution fallback level and the browser used. * * @param cameraResolutionConstraint The resolution constraint. * @returns The resulting *getUserMedia* *video* parameters. */ function getUserMediaVideoParams(cameraResolutionConstraint) { var userMediaVideoParams = { // @ts-ignore resizeMode: "none", }; switch (cameraResolutionConstraint) { case cameraManager_1.CameraResolutionConstraint.ULTRA_HD: return tslib_1.__assign(tslib_1.__assign({}, userMediaVideoParams), { width: { min: 3200, ideal: 3840, max: 4096 }, height: { min: 1800, ideal: 2160, max: 2400 } }); case cameraManager_1.CameraResolutionConstraint.FULL_HD: return tslib_1.__assign(tslib_1.__assign({}, userMediaVideoParams), { width: { min: 1400, ideal: 1920, max: 2160 }, height: { min: 900, ideal: 1080, max: 1440 } }); case cameraManager_1.CameraResolutionConstraint.HD: return tslib_1.__assign(tslib_1.__assign({}, userMediaVideoParams), { width: { min: 960, ideal: 1280, max: 1440 }, height: { min: 480, ideal: 720, max: 960 } }); case cameraManager_1.CameraResolutionConstraint.SD: return tslib_1.__assign(tslib_1.__assign({}, userMediaVideoParams), { width: { min: 640, ideal: 640, max: 800 }, height: { min: 480, ideal: 480, max: 600 } }); case cameraManager_1.CameraResolutionConstraint.NONE: default: return {}; } } /** * @hidden * * Try to access a given camera for video input at the given resolution level. * * If a camera is inaccessible because of errors, then it's added to the inaccessible device list. If the specific * error is of type `OverconstrainedError` or `NotReadableError` however, this procedure is done later on via a * separate external logic; also, in case of an error of type `NotAllowedError` (permission denied) this procedure is * not executed, in order to possibly recover if and when the user allows the camera to be accessed again. * This is done to allow checking if the camera can still be accessed via an updated deviceId when deviceId * information changes, or if it should then be confirmed to be considered inaccessible. * * Depending on parameters, device features and user permissions for camera access, any of the following errors * could be the rejected result of the returned promise: * - `AbortError` * - `NotAllowedError` * - `NotFoundError` * - `NotReadableError` * - `SecurityError` * - `OverconstrainedError` * * @param cameraResolutionConstraint The resolution constraint. * @param camera The camera to try to access for video input. * @returns A promise resolving to the `MediaStream` object coming from the accessed camera. */ function accessCameraStream(cameraResolutionConstraint, camera) { return tslib_1.__awaiter(this, void 0, void 0, function () { var getUserMediaParams, mediaStream, error_2; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: logger_1.Logger.log(logger_1.Logger.Level.DEBUG, "Attempt to access camera (camera):", camera); getUserMediaParams = { audio: false, video: getUserMediaVideoParams(cameraResolutionConstraint), }; // If it's the initial camera, use the given cameraType, otherwise use the given deviceId if (camera.deviceId === "") { getUserMediaParams.video.facingMode = { ideal: camera.cameraType === camera_1.Camera.Type.BACK ? "environment" : "user", }; } else { getUserMediaParams.video.deviceId = { exact: camera.deviceId, }; } _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, getUserMediaDelayed(getUserMediaParams)]; case 2: mediaStream = _a.sent(); adjustCameraFromMediaStream(mediaStream, camera); return [2 /*return*/, mediaStream]; case 3: error_2 = _a.sent(); mapNonStandardErrorName(error_2); if (!["OverconstrainedError", "NotReadableError", "NotAllowedError"].includes(error_2.name)) { markCameraAsInaccessible(camera); } throw error_2; case 4: return [2 /*return*/]; } }); }); } CameraAccess.accessCameraStream = accessCameraStream; /** * @hidden * * Mark a camera to be inaccessible and thus excluded from the camera list returned by [[getCameras]]. * * @param camera The camera to mark to be inaccessible. */ function markCameraAsInaccessible(camera) { // If it's the initial camera, do nothing if (camera.deviceId !== "") { logger_1.Logger.log(logger_1.Logger.Level.DEBUG, "Camera marked to be inaccessible:", camera); CameraAccess.inaccessibleDeviceIds.add(camera.deviceId); } } CameraAccess.markCameraAsInaccessible = markCameraAsInaccessible; /** * @hidden * * Get a list of available video devices in a cross-browser compatible way. * * @returns A promise resolving to the `MediaDeviceInfo` array of all available video devices. */ function enumerateVideoDevices() { var _a; return tslib_1.__awaiter(this, void 0, void 0, function () { var devices, _b; return tslib_1.__generator(this, function (_c) { switch (_c.label) { case 0: if (!(typeof navigator.enumerateDevices === "function")) return [3 /*break*/, 2]; return [4 /*yield*/, navigator.enumerateDevices()]; case 1: devices = _c.sent(); return [3 /*break*/, 7]; case 2: if (!(typeof navigator.mediaDevices === "object" && typeof navigator.mediaDevices.enumerateDevices === "function")) return [3 /*break*/, 4]; return [4 /*yield*/, navigator.mediaDevices.enumerateDevices()]; case 3: devices = _c.sent(); return [3 /*break*/, 7]; case 4: _c.trys.push([4, 6, , 7]); if (((_a = window.MediaStreamTrack) === null || _a === void 0 ? void 0 : _a.getSources) == null) { throw new Error(); } return [4 /*yield*/, new Promise(function (resolve) { var _a, _b; (_b = (_a = window.MediaStreamTrack) === null || _a === void 0 ? void 0 : _a.getSources) === null || _b === void 0 ? void 0 : _b.call(_a, resolve); })]; case 5: devices = _c.sent(); devices = devices .filter(function (device) { return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput"; }) .map(function (device) { var _a; return { deviceId: (_a = device.deviceId) !== null && _a !== void 0 ? _a : "", groupId: device.groupId, kind: "videoinput", label: device.label, toJSON: /* istanbul ignore next */ function () { return this; }, }; }); return [3 /*break*/, 7]; case 6: _b = _c.sent(); throw new unsupportedBrowserError_1.UnsupportedBrowserError({ fullSupport: false, scannerSupport: true, missingFeatures: [browserCompatibility_1.BrowserCompatibility.Feature.MEDIA_DEVICES], }); case 7: return [2 /*return*/, devices.filter(function (device) { return device.kind === "videoinput"; })]; } }); }); } })(CameraAccess = exports.CameraAccess || (exports.CameraAccess = {})); //# sourceMappingURL=cameraAccess.js.map