scandit-sdk
Version:
Scandit Barcode Scanner SDK for the Web
464 lines • 18.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var browserCompatibility_1 = require("./browserCompatibility");
var browserHelper_1 = require("./browserHelper");
var camera_1 = require("./camera");
var unsupportedBrowserError_1 = require("./unsupportedBrowserError");
/**
* A helper object to interact with cameras.
*/
var CameraAccess;
(function (CameraAccess) {
/**
* @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
*/
var cameraObjects = new Map();
/**
* @hidden
*/
var getCamerasPromise;
/**
* @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
*
* Adjusts the cameras' type classification based on the given currently active video stream:
* If the stream comes from an environment-facing camera, the camera is marked to be a back-facing camera
* and the other cameras to be of other types accordingly (if they are not correctly set already).
*
* The method returns the currently active camera if it's actually the main (back or only) camera in use.
*
* @param mediaStreamTrack The currently active `MediaStreamTrack`.
* @param cameras The array of available [[Camera]] objects.
* @returns Whether the stream was actually from the main camera.
*/
function adjustCamerasFromMainCameraStream(mediaStreamTrack, cameras) {
var mediaTrackSettings;
if (typeof mediaStreamTrack.getSettings === "function") {
mediaTrackSettings = mediaStreamTrack.getSettings();
}
var activeCamera = cameras.find(function (camera) {
return ((mediaTrackSettings != null && camera.deviceId === mediaTrackSettings.deviceId) ||
camera.label === mediaStreamTrack.label);
});
if (activeCamera !== undefined) {
var activeCameraIsBackFacing = (mediaTrackSettings != null && mediaTrackSettings.facingMode === "environment") ||
isBackCameraLabel(mediaStreamTrack.label);
var activeCameraIsMainBackCamera = activeCameraIsBackFacing;
// TODO: also correct camera types when active camera is not back-facing
if (activeCameraIsBackFacing && cameras.length > 1) {
// Correct camera types if needed
cameras.forEach(function (camera) {
if (camera.deviceId === activeCamera.deviceId) {
// tslint:disable-next-line:no-any
camera.cameraType = camera_1.Camera.Type.BACK;
}
else if (!isBackCameraLabel(camera.label)) {
// tslint:disable-next-line:no-any
camera.cameraType = camera_1.Camera.Type.FRONT;
}
});
var mainBackCamera = cameras
.filter(function (camera) {
return camera.cameraType === camera_1.Camera.Type.BACK;
})
.sort(function (camera1, camera2) {
return camera1.label.localeCompare(camera2.label);
})[0];
activeCameraIsMainBackCamera = activeCamera.deviceId === mainBackCamera.deviceId;
}
if (cameras.length === 1 || activeCameraIsMainBackCamera) {
return activeCamera;
}
}
return undefined;
}
CameraAccess.adjustCamerasFromMainCameraStream = adjustCamerasFromMainCameraStream;
/**
* @hidden
*
* @param devices The list of available devices.
* @returns The extracted list of camera objects initialized from the given devices.
*/
function extractCamerasFromDevices(devices) {
var cameras = devices
.filter(function (device) {
return device.kind === "videoinput";
})
.map(function (videoDevice) {
if (cameraObjects.has(videoDevice.deviceId)) {
return cameraObjects.get(videoDevice.deviceId);
}
var label = videoDevice.label != null ? videoDevice.label : "";
var camera = {
deviceId: videoDevice.deviceId,
label: label,
cameraType: isBackCameraLabel(label) ? camera_1.Camera.Type.BACK : camera_1.Camera.Type.FRONT
};
if (label !== "") {
cameraObjects.set(videoDevice.deviceId, camera);
}
return camera;
});
if (cameras.length > 1 &&
!cameras.some(function (camera) {
return camera.cameraType === camera_1.Camera.Type.BACK;
})) {
// Check if cameras are labeled with resolution information, take the higher-resolution one in that case
// Otherwise pick the last camera
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.__spread(cameraResolutions)));
}
// tslint:disable-next-line:no-any
cameras[backCameraIndex].cameraType = camera_1.Camera.Type.BACK;
}
return cameras;
}
/**
* 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.
*
* Depending on device features and user permissions for camera access, any of the following errors
* could be the rejected result of the returned promise:
* - `UnsupportedBrowserError`
* - `PermissionDeniedError`
* - `NotAllowedError`
* - `NotFoundError`
* - `AbortError`
* - `NotReadableError`
* - `InternalError`
*
* @returns A promise resolving to the array of available [[Camera]] objects (could be empty).
*/
function getCameras() {
var _this = this;
if (getCamerasPromise != null) {
return getCamerasPromise;
}
var browserCompatibility = browserHelper_1.BrowserHelper.checkBrowserCompatibility();
if (!browserCompatibility.fullSupport) {
return Promise.reject(new unsupportedBrowserError_1.UnsupportedBrowserError(browserCompatibility));
}
var accessPermissionPromise = new Promise(function (resolve, reject) {
return enumerateDevices()
.then(function (devices) {
if (devices
.filter(function (device) {
return device.kind === "videoinput";
})
.every(function (device) {
return device.label === "";
})) {
resolve(navigator.mediaDevices.getUserMedia({
video: true,
audio: false
}));
}
else {
resolve();
}
})
.catch(reject);
});
getCamerasPromise = new Promise(function (resolve, reject) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var stream, devices, cameras, error_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, 4, 5]);
return [4 /*yield*/, accessPermissionPromise];
case 1:
stream = _a.sent();
return [4 /*yield*/, enumerateDevices()];
case 2:
devices = _a.sent();
cameras = extractCamerasFromDevices(devices);
console.debug.apply(console, tslib_1.__spread(["Camera list: "], cameras));
return [2 /*return*/, resolve(cameras)];
case 3:
error_1 = _a.sent();
// istanbul ignore if
if (error_1.name === "SourceUnavailableError") {
error_1.name = "NotReadableError";
}
return [2 /*return*/, reject(error_1)];
case 4:
// istanbul ignore else
if (stream != null) {
stream.getVideoTracks().forEach(function (track) {
track.stop();
});
}
getCamerasPromise = undefined;
return [7 /*endfinally*/];
case 5: return [2 /*return*/];
}
});
}); });
return getCamerasPromise;
}
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) {
console.debug("Camera access:", getUserMediaParams.video);
return new Promise(function (resolve, reject) {
window.setTimeout(function () {
navigator.mediaDevices
.getUserMedia(getUserMediaParams)
.then(resolve)
.catch(reject);
}, 0);
});
}
/**
* @hidden
*
* Get the *getUserMedia* *video* parameters to be used given a resolution fallback level and the browser used.
*
* @param resolutionFallbackLevel The number representing the wanted resolution, from 0 to 6,
* resulting in higher to lower video resolutions.
* @param isSafariBrowser Whether the browser is *Safari*.
* @returns The resulting *getUserMedia* *video* parameters.
*/
function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
switch (resolutionFallbackLevel) {
case 0:
if (isSafariBrowser) {
return {
width: { min: 1400, ideal: 1920, max: 1920 },
height: { min: 900, ideal: 1080, max: 1440 }
};
}
else {
return {
width: { min: 1400, ideal: 1920, max: 1920 },
height: { min: 900, ideal: 1440, max: 1440 }
};
}
case 1:
if (isSafariBrowser) {
return {
width: { min: 1200, ideal: 1600, max: 1920 },
height: { min: 900, ideal: 1080, max: 1200 }
};
}
else {
return {
width: { min: 1200, ideal: 1920, max: 1920 },
height: { min: 900, ideal: 1200, max: 1200 }
};
}
case 2:
if (isSafariBrowser) {
return {
width: { min: 1080, ideal: 1600, max: 1920 },
height: { min: 900, ideal: 900, max: 1080 }
};
}
else {
return {
width: { min: 1080, ideal: 1920, max: 1920 },
height: { min: 900, ideal: 1080, max: 1080 }
};
}
case 3:
if (isSafariBrowser) {
return {
width: { min: 960, ideal: 1280, max: 1440 },
height: { min: 480, ideal: 720, max: 960 }
};
}
else {
return {
width: { min: 960, ideal: 1280, max: 1440 },
height: { min: 480, ideal: 960, max: 960 }
};
}
case 4:
if (isSafariBrowser) {
return {
width: { min: 720, ideal: 1024, max: 1440 },
height: { min: 480, ideal: 768, max: 768 }
};
}
else {
return {
width: { min: 720, ideal: 1280, max: 1440 },
height: { min: 480, ideal: 720, max: 768 }
};
}
case 5:
if (isSafariBrowser) {
return {
width: { min: 640, ideal: 800, max: 1440 },
height: { min: 480, ideal: 600, max: 720 }
};
}
else {
return {
width: { min: 640, ideal: 960, max: 1440 },
height: { min: 480, ideal: 720, max: 720 }
};
}
default:
return {};
}
}
/**
* @hidden
*
* Try to access a given camera for video input at the given resolution level.
*
* @param resolutionFallbackLevel The number representing the wanted resolution, from 0 to 6,
* resulting in higher to lower video resolutions.
* @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(resolutionFallbackLevel, camera) {
var browserName = browserHelper_1.BrowserHelper.userAgentInfo.getBrowser().name;
var getUserMediaParams = {
audio: false,
video: getUserMediaVideoParams(resolutionFallbackLevel, browserName != null && browserName.includes("Safari"))
};
if (camera.deviceId === "") {
getUserMediaParams.video.facingMode = {
ideal: camera.cameraType === camera_1.Camera.Type.BACK ? "environment" : "user"
};
}
else {
getUserMediaParams.video.deviceId = {
exact: camera.deviceId
};
}
return getUserMediaDelayed(getUserMediaParams);
}
CameraAccess.accessCameraStream = accessCameraStream;
/**
* @hidden
*
* Get a list of available devices in a cross-browser compatible way.
*
* @returns A promise resolving to the `MediaDeviceInfo` array of all available devices.
*/
function enumerateDevices() {
if (typeof navigator.enumerateDevices === "function") {
return navigator.enumerateDevices();
}
else if (typeof navigator.mediaDevices === "object" &&
typeof navigator.mediaDevices.enumerateDevices === "function") {
return navigator.mediaDevices.enumerateDevices();
}
else {
return new Promise(function (resolve, reject) {
try {
if (window.MediaStreamTrack == null || window.MediaStreamTrack.getSources == null) {
throw new Error();
}
window.MediaStreamTrack.getSources(function (devices) {
resolve(devices
.filter(function (device) {
return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput";
})
.map(function (device) {
return {
deviceId: device.deviceId != null ? device.deviceId : "",
groupId: device.groupId,
kind: "videoinput",
label: device.label,
toJSON: /* istanbul ignore next */ function () {
return this;
}
};
}));
});
}
catch (_a) {
var browserCompatibility = {
fullSupport: false,
scannerSupport: true,
missingFeatures: [browserCompatibility_1.BrowserCompatibility.Feature.MEDIA_DEVICES]
};
return reject(new unsupportedBrowserError_1.UnsupportedBrowserError(browserCompatibility));
}
});
}
}
})(CameraAccess = exports.CameraAccess || (exports.CameraAccess = {}));
//# sourceMappingURL=cameraAccess.js.map