From ba65d90627a687b1280af2cb06b09cc8add9db47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Kol=C3=A1=C5=99?= Date: Tue, 31 Jan 2023 11:59:40 +0100 Subject: [PATCH] camera input select --- src/nodes/Webcam.js | 56 --------------------------- src/nodes/Webcam.ts | 94 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 56 deletions(-) delete mode 100644 src/nodes/Webcam.js create mode 100644 src/nodes/Webcam.ts diff --git a/src/nodes/Webcam.js b/src/nodes/Webcam.js deleted file mode 100644 index 5603adc..0000000 --- a/src/nodes/Webcam.js +++ /dev/null @@ -1,56 +0,0 @@ -import Node from './Node'; - -export default class Webcam extends Node { - - constructor(options) { - super('Webcam', {}, { - image: { - type: 'Image' - }, - width: { - type: 'Number', - }, - height: { - type: 'Number', - } - }); - this.start() - .catch(console.error.bind(console)) - } - - - async start() { - const devices = (await navigator.mediaDevices.enumerateDevices()).filter((d) => d.kind === 'videoinput') - const last = (ary) => ary[ary.length - 1]; - const stream = await navigator.mediaDevices.getUserMedia({ - video: { - width: { min: 480, ideal: 1920, max: 1920 }, - height: { min: 320, ideal: 1080, max: 1080 }, - deviceId: last(devices).deviceId - }, - }); - /** @type {HTMLVideoElement} */ - - const video = document.createElement('video'); - // @ts-ignore - video.playsinline = true; - video.srcObject = stream; - video.play(); - let lastFrameTime = null; - const feedLoop = () => { - const currentTime = video.currentTime; - if (lastFrameTime !== currentTime) { - lastFrameTime = currentTime; - this.out.image.value = video; - this.out.width.value = video.videoWidth; - this.out.height.value = video.videoHeight; - } - if (!this._stopped) { - requestAnimationFrame(feedLoop); - } else { - video.src = null; - } - }; - feedLoop(); - } -} \ No newline at end of file diff --git a/src/nodes/Webcam.ts b/src/nodes/Webcam.ts new file mode 100644 index 0000000..6ed55f6 --- /dev/null +++ b/src/nodes/Webcam.ts @@ -0,0 +1,94 @@ +import Node from './Node'; +import {last} from 'lodash/array'; +import {find} from 'lodash/collection'; +import {NumberVar, StringVar, ImageVar} from './io/AbstractIOSet'; + +const outputs = { + image: { + type: 'Image' + } as ImageVar, + width: { + type: 'Number', + } as NumberVar, + height: { + type: 'Number', + } as NumberVar, + camera: { + type: 'String', + enum: [], + } as StringVar +} + +export default class Webcam extends Node { + + devices: MediaDeviceInfo[]; + currentDeviceId: string; + video: HTMLVideoElement + constructor(options) { + super('Webcam', {}, outputs); + this.start() + .catch(console.error.bind(console)) + } + + async start() { + this.devices = (await navigator.mediaDevices.enumerateDevices()).filter((d) => d.kind === 'videoinput'); + this.out.camera.__definition.enum = this.devices.map(device => device.label) + const deviceId = last(this.devices).deviceId; + this.out.camera.value = this.getDeviceById(deviceId).label; + + await this.startDevice(deviceId) + + this.out.camera.onchange(async (label) => { + this.video = null; + const selectedDeviceId = this.getDeviceByLabel(label).deviceId; + await this.startDevice(selectedDeviceId) + }) + } + + private async startDevice(deviceId) { + this.currentDeviceId = deviceId; + const stream = await navigator.mediaDevices.getUserMedia({ + video: { + width: { min: 480, ideal: 1920, max: 1920 }, + height: { min: 320, ideal: 1080, max: 1080 }, + deviceId + }, + }); + /** @type {HTMLVideoElement} */ + this.video = document.createElement('video'); + // @ts-ignore + this.video.playsinline = true; + this.video.srcObject = stream; + this.video.play(); + let lastFrameTime = null; + const feedLoop = () => { + if (!this.video) { + return; + } + const currentTime = this.video.currentTime; + if (lastFrameTime !== currentTime) { + lastFrameTime = currentTime; + // @ts-ignore + this.out.image.value = this.video; + // @ts-ignore + this.out.width.value = this.video.videoWidth; + // @ts-ignore + this.out.height.value = this.video.videoHeight; + } + if (!this._stopped) { + requestAnimationFrame(feedLoop); + } else { + this.video.src = null; + } + }; + feedLoop(); + } + + private getDeviceByLabel(label: string) { + return find(this.devices, device => device.label === label); + } + + private getDeviceById(id: string) { + return find(this.devices, device => device.deviceId === id); + } +} \ No newline at end of file