/** * @fileoverview * Libraries associated with retrieving cameras. * * @author mebjas */ import { CameraDevice } from "./core"; import { Html5QrcodeStrings } from "../strings"; /** Class for retrieving cameras on the device. */ export class CameraRetriever { /** Returns list of {@link CameraDevice} supported by the device. */ public static retrieve(): Promise> { if (navigator.mediaDevices) { return CameraRetriever.getCamerasFromMediaDevices(); } // Using deprecated api to support really old browsers. var mst = MediaStreamTrack; if (MediaStreamTrack && mst.getSources) { return CameraRetriever.getCamerasFromMediaStreamTrack(); } return CameraRetriever.rejectWithError(); } private static rejectWithError(): Promise> { // This can potentially happen if the page is loaded without SSL. let errorMessage = Html5QrcodeStrings.unableToQuerySupportedDevices(); if (!CameraRetriever.isHttpsOrLocalhost()) { errorMessage = Html5QrcodeStrings.insecureContextCameraQueryError(); } return Promise.reject(errorMessage); } private static isHttpsOrLocalhost(): boolean { if (location.protocol === "https:") { return true; } const host = location.host.split(":")[0]; return host === "127.0.0.1" || host === "localhost"; } private static async getCamerasFromMediaDevices(): Promise> { // Hacky approach to close any active stream if they are active. const closeActiveStreams = (stream: MediaStream) => { const tracks = stream.getVideoTracks(); for (const track of tracks) { track.enabled = false; track.stop(); stream.removeTrack(track); } }; // This should trigger the permission flow if required. let mediaStream = await navigator.mediaDevices.getUserMedia( { audio: false, video: true }); let devices = await navigator.mediaDevices.enumerateDevices(); let results: Array = []; for (const device of devices) { if (device.kind === "videoinput") { results.push({ id: device.deviceId, label: device.label }); } } closeActiveStreams(mediaStream); return results; } private static getCamerasFromMediaStreamTrack() : Promise> { return new Promise((resolve, _) => { const callback = (sourceInfos: Array) => { const results: Array = []; for (const sourceInfo of sourceInfos) { if (sourceInfo.kind === "video") { results.push({ id: sourceInfo.id, label: sourceInfo.label }); } } resolve(results); } var mst = MediaStreamTrack; mst.getSources(callback); }); } }