UNPKG

@huddly/device-api-usb

Version:

Huddly SDK device api which uses node-usb wrapper responsible for handling the transport layer of the communication and discovering the physical device/camera

171 lines 8.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const usb_1 = require("usb"); const HuddlyHex_1 = __importDefault(require("@huddly/sdk-interfaces/lib/enums/HuddlyHex")); const Logger_1 = __importDefault(require("@huddly/sdk-interfaces/lib/statics/Logger")); const index_1 = require("./index"); class DeviceDiscoveryManager { constructor(pidsToIgnore) { this.className = 'Device-API-USB Manager'; this.attachedDevices = []; this.pidsToIgnore = pidsToIgnore || index_1.defaultPidsToIgnore; } newDeviceAttached(device) { this.attachedDevices.push(device); } newDeviceDetached(detachedDev) { this.attachedDevices = this.attachedDevices.filter((device) => device.id !== detachedDev.id); } isDeviceWithUUIDCached(uuid) { return this.attachedDevices.some((dev) => dev.id === uuid); } get cachedDevices() { return this.attachedDevices; } generateUsbUniqueId(props) { const stringCombo = String(props.usbBusNumber).concat(String(props.usbDeviceAddress).concat(props.usbPortNumbers.toString())); let hash = 0; for (let i = 0; i < stringCombo.length; i++) { const char = stringCombo.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return hash.toString(); } getDeviceUUID(device) { const uid = this.generateUsbUniqueId({ usbBusNumber: device.busNumber, usbDeviceAddress: device.deviceAddress, usbPortNumbers: device.portNumbers, }); return uid; } fetchAndPopulateDeviceParams(device) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { try { device.open(); device.getStringDescriptor(device.deviceDescriptor.iSerialNumber, (err, serialNo) => { if (err) return reject(err); device.getStringDescriptor(device.deviceDescriptor.iProduct, (err, productName) => { if (err) return reject(err); device['id'] = this.getDeviceUUID(device); device['serialNumber'] = serialNo; device['productName'] = productName; device['productId'] = device.deviceDescriptor.idProduct; device['vendorId'] = device.deviceDescriptor.idVendor; device.close(); resolve(true); }); }); } catch (e) { if ([usb_1.usb.LIBUSB_ERROR_ACCESS, usb_1.usb.LIBUSB_ERROR_BUSY].includes(e.errno)) { Logger_1.default.warn(`Unable to open usb device. Device occupied by another process!`, this.className); return resolve(false); // We assume that a different SDK process is using this device. We don't fail here. } Logger_1.default.warn(`Unable to fetch device parameters from usb descriptor! ${e}`, this.className); reject(`Unable to fetch device parameters from usb descriptor! ${e}`); } }); }); } registerForHotplugEvents(eventEmitter) { this.eventEmitter = eventEmitter; usb_1.usb.on('attach', (device) => __awaiter(this, void 0, void 0, function* () { if (this.isValidHuddlyDevice(device)) { if (yield this.fetchAndPopulateDeviceParams(device)) { this.newDeviceAttached(device); Logger_1.default.debug(`Got ATTACH event from device with serial ${device.serialNumber}`, this.className); this.eventEmitter.emit('ATTACH', device); } } })); usb_1.usb.on('detach', (device) => { if (this.isValidHuddlyDevice(device)) { Logger_1.default.debug(`Got DETACH event from device with serial ${device.serialNumber}`, this.className); this.newDeviceDetached(device); this.eventEmitter.emit('DETACH', device); } }); } /** * Helper method for aiding unit testing of device list feature * * @return {*} {usb.Device[]} A list of usb devices returned by node-usb library * @memberof DeviceDiscoveryManager */ getUnfilteredDeviceList() { return (0, usb_1.getDeviceList)(); } deviceList(doEmitNewDevices = false) { return __awaiter(this, void 0, void 0, function* () { const usbDevices = this.getUnfilteredDeviceList(); const devices = usbDevices.filter((dev) => this.isValidHuddlyDevice(dev)); const foundDevices = []; for (let idx = 0; idx < devices.length; idx++) { const uuid = this.getDeviceUUID(devices[idx]); if (!this.isDeviceWithUUIDCached(uuid) && (yield this.fetchAndPopulateDeviceParams(devices[idx]))) { this.newDeviceAttached(devices[idx]); // We assume that the device "Detach" event will take care of clearing up the device cache list if (doEmitNewDevices) { this.eventEmitter.emit('ATTACH', devices[idx]); } } // After having generated the UUID and populated the device parameters, add it to the found device list foundDevices.push(devices[idx]); } return foundDevices; }); } getDevice(serialNumber) { return __awaiter(this, void 0, void 0, function* () { const devices = yield this.deviceList(); Logger_1.default.debug(`DeviceList found ${devices.length} enumerated Huddly devices`, this.className); if (serialNumber) { Logger_1.default.debug(`Filtering the devices for the following serial number: ${serialNumber}`, this.className); const targetDevice = devices.find((element) => { const dev = element; if (dev && dev.serialNumber) { // Could be that the device was not opened for the serial number to be read, hence the check here. return dev.serialNumber.includes(serialNumber) || serialNumber.includes(dev.serialNumber); } return false; }); if (!targetDevice) { Logger_1.default.warn(`Unable to find device with serial ${serialNumber} among ${devices.length} huddly devices attached on the host machine!`, this.className); } return targetDevice; } else if (devices.length > 0) { if (devices.length > 1) { Logger_1.default.warn(`More than 1 Huddly device discovered! No target serial specified, picking the first device out of ${devices.length} devices.`, this.className); } return devices[0]; } Logger_1.default.warn(`Could not find device with serial ${serialNumber} amongst ${devices.length} devices!`, this.className); return undefined; }); } isValidHuddlyDevice(device) { return (device.deviceDescriptor.idVendor === HuddlyHex_1.default.VID && !this.pidsToIgnore.includes(device.deviceDescriptor.idProduct)); } } exports.default = DeviceDiscoveryManager; //# sourceMappingURL=manager.js.map