@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
JavaScript
"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