├── .gitignore
├── .npmrc
├── examples
├── stream-disk
│ ├── .gitignore
│ ├── README.md
│ └── stream-disk.js
├── reference-implementation
│ ├── stream-disk
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── stream-disk.js
│ └── README.md
├── display-details
│ ├── README.md
│ └── display-details.js
└── README.md
├── .editorconfig
├── index.js
├── src
├── utilities
│ ├── constants.js
│ ├── assert-libuvc-result.js
│ ├── get-pointer.js
│ ├── libuvc-error.js
│ └── get-pointers.js
├── index.js
├── frame-stream.js
├── device-handle.js
├── libuvc.js
├── device.js
├── device-descriptor.js
├── libuvc-standard-units.js
├── context.js
├── controls.js
└── frame-streamer.js
├── package.json
├── README.md
├── LICENSE
└── data
└── standard-units.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/examples/stream-disk/.gitignore:
--------------------------------------------------------------------------------
1 | stream-disk.mjpeg
2 | stream-disk.mp4
3 | stream-disk.frame-*.jpg
4 |
--------------------------------------------------------------------------------
/examples/reference-implementation/stream-disk/.gitignore:
--------------------------------------------------------------------------------
1 | stream-disk.mjpeg
2 | stream-disk.mp4
3 | stream-disk.frame-*.jpg
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | insert_final_newline = true
5 | charset = utf-8
6 | trim_trailing_whitespace = true
7 | end_of_line = lf
8 |
9 | [*.{js,json,md}]
10 | indent_style = space
11 |
12 | [*.{js}]
13 | indent_size = 4
14 |
15 | [*.{json,md}]
16 | indent_size = 2
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
--------------------------------------------------------------------------------
/examples/reference-implementation/README.md:
--------------------------------------------------------------------------------
1 | # [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Reference implementations
2 |
3 | Because the code spans multiple languages as well as hardware, some lower-level examples/tests are in place to reduce system-dependent uncertainty.
4 |
5 | ---
6 |
7 | [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Copyright © 2020, 2021 [Joel Purra](https://joelpurra.com/). Released under [GNU Lesser General Public License version 3.0 (LGPL-3.0)](https://www.gnu.org/licenses/lgpl.html). [Your donations are appreciated!](https://joelpurra.com/donate/)
8 |
--------------------------------------------------------------------------------
/examples/display-details/README.md:
--------------------------------------------------------------------------------
1 | # [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Display all camera details
2 |
3 | Detects cameras, iterates over all available camera details, and prints them to screen.
4 |
5 | # Requirements
6 |
7 | - Libraries:
8 | - [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6) for the Node.js wrapper.
9 | - [`libuvc`](https://ken.tossell.net/libuvc/) for capturing the stream of images.
10 | - Optional:
11 | - One or more supported, and connected, UVC cameras.
12 |
13 | # Usage
14 |
15 | ```shell
16 | node display-details.js
17 | ```
18 |
19 | # Output
20 |
21 | An unstructured set of available data for all available UVC cameras.
22 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | module.exports = require("./src/");
20 |
--------------------------------------------------------------------------------
/src/utilities/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | module.exports = {
20 | maxNumberOfUvcDevices: 100,
21 | };
22 |
--------------------------------------------------------------------------------
/src/utilities/assert-libuvc-result.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const LibuvcError = require("./libuvc-error");
20 |
21 | module.exports = function assertLibuvcResult(libuvc, result, message) {
22 | if (result < 0) {
23 | throw new LibuvcError(libuvc, result, message);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/src/utilities/get-pointer.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const ref = require("ref-napi");
20 |
21 | module.exports = function getPointer(firstPointerPointer) {
22 | const nonNullPointer = ref.reinterpretUntilZeros(firstPointerPointer, 1);
23 |
24 | if (nonNullPointer.length === 0) {
25 | return null;
26 | }
27 |
28 | const pointer = ref.reinterpret(nonNullPointer, ref.types.size_t.size, 0);
29 |
30 | return pointer;
31 | };
32 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | module.exports = {
20 | Context: require("./context"),
21 | Controls: require("./controls"),
22 | Device: require("./device"),
23 | DeviceDescriptor: require("./device-descriptor"),
24 | DeviceHandle: require("./device-handle"),
25 | FrameStream: require("./frame-stream"),
26 | FrameStreamer: require("./frame-streamer"),
27 | LibUvc: require("./libuvc"),
28 | LibuvcStandardUnits: require("./libuvc-standard-units"),
29 | };
30 |
--------------------------------------------------------------------------------
/examples/reference-implementation/stream-disk/README.md:
--------------------------------------------------------------------------------
1 | # [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Stream webcam video to disk
2 |
3 | Checks that a compatible camera is connected, stream 10 seconds of video (image frames), and saves them to a file on disk.
4 |
5 | This is the [`libuvc` documentation example](https://ken.tossell.net/libuvc/doc/) by [Ken Tossell](https://ken.tossell.net/), but it saves the video to disk.
6 |
7 | # Requirements
8 |
9 | - Libraries:
10 | - [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6) for the Node.js wrapper.
11 | - [`libuvc`](https://ken.tossell.net/libuvc/) for capturing the stream of images.
12 | - Optional:
13 | - [`ffmpeg`](https://ffmpeg.org/) to convert from [Motion JPEG](https://en.wikipedia.org/wiki/Motion_JPEG) to a different format.
14 | - A supported, and connected, UVC camera.
15 |
16 | # Usage
17 |
18 | See the terminal for some debugging output, and the output file `stream-disk.mjpeg`.
19 |
20 | ```shell
21 | node stream-disk.js
22 | ```
23 |
24 | # Output
25 |
26 | The output file is `stream-disk.mjpeg`. See [file formats](../README.md#file-formats) for convenient conversions.
27 |
28 | ---
29 |
30 | [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Copyright © 2020, 2021 [Joel Purra](https://joelpurra.com/). Released under [GNU Lesser General Public License version 3.0 (LGPL-3.0)](https://www.gnu.org/licenses/lgpl.html). [Your donations are appreciated!](https://joelpurra.com/donate/)
31 |
--------------------------------------------------------------------------------
/src/utilities/libuvc-error.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | module.exports = class LibuvcError extends Error {
22 | errno = null;
23 |
24 | constructor(libuvc, errno, message) {
25 | super(message);
26 |
27 | assert(typeof errno === "number");
28 | assert(typeof message === "string");
29 | assert(message.length > 0);
30 |
31 | this.errno = errno;
32 |
33 | Object.defineProperty(this, "code", {
34 | enumerable: true,
35 | value:
36 | String(libuvc.constants.uvc_error[this.errno]) || this.errno.toString(),
37 | });
38 | }
39 |
40 | toString() {
41 | return `${super.toString()} (${JSON.stringify(this.code)})`;
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/examples/stream-disk/README.md:
--------------------------------------------------------------------------------
1 | # [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Stream webcam video to disk
2 |
3 | Checks that a compatible camera is connected, stream 10 seconds of video image frames, and saves them to a file on disk.
4 |
5 | This is the [`libuvc` documentation example](https://ken.tossell.net/libuvc/doc/) by [Ken Tossell](https://ken.tossell.net/) using the [`node-uvc`](https://joelpurra.com/projects/node-uvc/) wrapper, but it saves the video to disk.
6 |
7 | # Requirements
8 |
9 | - Libraries:
10 | - [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6) for the Node.js wrapper.
11 | - [`libuvc`](https://ken.tossell.net/libuvc/) for capturing the stream of images.
12 | - Optional:
13 | - [`ffmpeg`](https://ffmpeg.org/) to convert from [Motion JPEG](https://en.wikipedia.org/wiki/Motion_JPEG) to a different format.
14 | - A supported, and connected, UVC camera.
15 |
16 | # Usage
17 |
18 | See the terminal for some debugging output, and the output file `stream-disk.mjpeg`.
19 |
20 | ```shell
21 | node stream-disk.js
22 | ```
23 |
24 | # Output
25 |
26 | The output file is `stream-disk.mjpeg`. See [file formats](../README.md#file-formats) for convenient conversions.
27 |
28 | ---
29 |
30 | [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Copyright © 2020, 2021 [Joel Purra](https://joelpurra.com/). Released under [GNU Lesser General Public License version 3.0 (LGPL-3.0)](https://www.gnu.org/licenses/lgpl.html). [Your donations are appreciated!](https://joelpurra.com/donate/)
31 |
--------------------------------------------------------------------------------
/src/utilities/get-pointers.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const ref = require("ref-napi");
20 |
21 | // TODO: use ref-array-di instead?
22 | module.exports = function getPointers(
23 | maxNumberOfPointers,
24 | firstPointerPointer
25 | ) {
26 | const nonNullPointers = ref.reinterpretUntilZeros(firstPointerPointer, 1);
27 | const pointers = [];
28 |
29 | for (let i = 0; i < maxNumberOfPointers; i++) {
30 | let offset = i * ref.types.size_t.size;
31 |
32 | if (offset >= nonNullPointers.length) {
33 | break;
34 | }
35 |
36 | const pointer = ref.reinterpret(
37 | nonNullPointers,
38 | ref.types.size_t.size,
39 | offset
40 | );
41 |
42 | pointers.push(pointer);
43 | }
44 |
45 | return pointers;
46 | };
47 |
--------------------------------------------------------------------------------
/src/frame-stream.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const { Transform } = require("stream");
20 | const ref = require("ref-napi");
21 |
22 | module.exports = class FrameStream extends Transform {
23 | constructor() {
24 | super({
25 | readableObjectMode: true,
26 | writableObjectMode: true,
27 | });
28 | }
29 |
30 | _transform(framePointer, encoding, callback) {
31 | const frame = framePointer.deref();
32 | const image = ref.reinterpret(frame.data, frame.data_bytes, 0);
33 |
34 | const frameObject = {
35 | image: image,
36 | width: frame.width,
37 | height: frame.height,
38 | format: frame.format,
39 | step: frame.step,
40 | sequence: frame.sequence,
41 | captureTime: {
42 | seconds: frame.capture_time.tv_sec,
43 | microseconds: frame.capture_time.tv_usec,
44 | },
45 | };
46 |
47 | return callback(null, frameObject);
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/src/device-handle.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const { intersection, without } = require("lodash");
22 |
23 | const ref = require("ref-napi");
24 | const libuvc = require("@ffi-libraries/libuvc-v0.0.6");
25 |
26 | const assertLibuvcResult = require("./utilities/assert-libuvc-result");
27 |
28 | module.exports = class DeviceHandle {
29 | libuvc = null;
30 | deviceHandle = null;
31 |
32 | constructor(libuvc, deviceHandle) {
33 | assert(libuvc);
34 | assert(deviceHandle);
35 |
36 | this.libuvc = libuvc;
37 | this.deviceHandle = deviceHandle;
38 | }
39 |
40 | async initialize() {
41 | assert.notStrictEqual(this.deviceHandle, null);
42 | }
43 |
44 | async uninitialize() {
45 | assert.notStrictEqual(this.libuvc, null);
46 | assert.notStrictEqual(this.deviceHandle, null);
47 |
48 | this.libuvc.functions.uvc_close(this.deviceHandle.deref());
49 | this.deviceHandle = null;
50 | }
51 |
52 | async getControlLength(unit, control) {
53 | assert(unit >= 0);
54 | assert(control >= 0);
55 |
56 | return this.libuvc.functions.uvc_get_ctrl_len(
57 | this.deviceHandle.deref(),
58 | unit,
59 | control
60 | );
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # [`node-uvc`](https://joelpurra.com/projects/node-uvc/) examples
2 |
3 | Examples are mainly written in javascript. There are also reference implementations to ensure the underlying libraries, such as [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6), work as expected.
4 |
5 | ## File formats
6 |
7 | Some examples output files in various formats.
8 |
9 | ### Motion JPEG
10 |
11 | The output file `stream-disk.mjpeg` is in [Motion JPEG](https://en.wikipedia.org/wiki/Motion_JPEG) (MJPEG) format. It is just a series of concatenated [JPEG](https://en.wikipedia.org/wiki/JPEG) images, and does not contain the usual video metadata, such as framerate. Your regular video player should be able to open it, but because it lacks a defined framerate, the "video" renders at maximum speed -- and you might only see a single frame.
12 |
13 | It is often possible to losslessly extract each frame as a JPEG image. Some cameras might have a slightly different MJPEG format where some repeated JPEG data is optimized away, which might not work with the lossless "copy" -- in that case, try removing `-vcodec copy` to recompress each JPEG image.
14 |
15 | Note that the below command will produce hundreds of JPEG images, named `stream-disk.frame-0001.jpg` and so on.
16 |
17 | ```shell
18 | ffmpeg -f mjpeg -i stream-disk.mjpeg -vcodec copy stream-disk.frame-%4d.jpg
19 | ```
20 |
21 | The stream is captured at 30 frames per second (but may vary dynamically, depending on your camera and scene), so it can be converted to another video format which has this metadata. This will also generally reduce the file size significantly, as most video formats do not store each full frame image but the only difference between subsequent frames.
22 |
23 | Use the below command to create `stream-disk.mp4`.
24 |
25 | ```shell
26 | ffmpeg -f mjpeg -framerate 30 -i stream-disk.mjpeg stream-disk.mp4
27 | ```
28 |
29 | ---
30 |
31 | [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Copyright © 2020, 2021 [Joel Purra](https://joelpurra.com/). Released under [GNU Lesser General Public License version 3.0 (LGPL-3.0)](https://www.gnu.org/licenses/lgpl.html). [Your donations are appreciated!](https://joelpurra.com/donate/)
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "uvc",
3 | "version": "1.0.0",
4 | "description": "Library for USB Video Class (UVC) devices. Used to write software for webcams, camcorders, etcetera.",
5 | "homepage": "https://joelpurra.com/projects/node-uvc/",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "npm run --silent lint:fast",
9 | "lint": "npm run --silent lint:full",
10 | "lint:fast": "npm run --silent lint:prettier && npm run --silent lint:copyright",
11 | "lint:full": "npm run --silent lint:fast",
12 | "lint:fix": "npm run --silent lint:prettier:fix",
13 | "lint:copyright": "find . -not \\( -path './.git/*' -or -path './node_modules/*' -or -path '*/reference-implementation/*' \\) -type f \\( -iname '*.js' -and -not \\( -path './.huskyrc.js' \\) \\) -print0 | xargs -0 grep -L 'This file is part of node-uvc' | sed 's/^/File is missing copyright notice: /'",
14 | "lint:prettier": "prettier --list-different './**/*.js' './**/*.json' './**/*.md'",
15 | "lint:prettier:fix": "prettier --write './**/*.js' './**/*.json' './**/*.md'"
16 | },
17 | "dependencies": {
18 | "@ffi-libraries/libuvc-v0.0.6": "github:node-ffi-libraries/node-ffi-library-libuvc-v0.0.6#semver:^2.0.1",
19 | "bluebird": "^3.7.2",
20 | "lodash": "^4.17.21",
21 | "yaml": "^1.10.2"
22 | },
23 | "devDependencies": {
24 | "ffi-napi": "^4.0.3",
25 | "husky": "^4.3.8",
26 | "prettier": "^2.3.1",
27 | "ref-array-di": "^1.2.2",
28 | "ref-napi": "^3.0.3",
29 | "ref-struct-di": "^1.1.1",
30 | "ref-union-di": "^1.0.1"
31 | },
32 | "engines": {
33 | "node": "^12.0.0 || ^14.0.0 || ^16.0.0"
34 | },
35 | "keywords": [
36 | "usb",
37 | "usb video class",
38 | "uvc",
39 | "camera",
40 | "webcamera",
41 | "video",
42 | "photo",
43 | "capture",
44 | "controls",
45 | "configuration",
46 | "libusb",
47 | "libuvc"
48 | ],
49 | "bugs": {
50 | "url": "https://github.com/joelpurra/node-uvc/issues"
51 | },
52 | "repository": {
53 | "type": "git",
54 | "url": "git+https://github.com/joelpurra/node-uvc.git"
55 | },
56 | "author": {
57 | "name": "Joel Purra",
58 | "email": "mig@joelpurra.se",
59 | "url": "https://joelpurra.com/"
60 | },
61 | "license": "LGPL-3.0",
62 | "husky": {
63 | "hooks": {
64 | "pre-commit": "npm run --silent test",
65 | "pre-push": "npm run --silent test"
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/libuvc.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const { fromPairs } = require("lodash");
22 |
23 | const ref = require("ref-napi");
24 | const { load } = require("@ffi-libraries/libuvc-v0.0.6");
25 |
26 | const Device = require("./device");
27 |
28 | const getPointer = require("./utilities/get-pointer");
29 | const getPointers = require("./utilities/get-pointers");
30 | const { maxNumberOfUvcDevices } = require("./utilities/constants");
31 |
32 | module.exports = class LibUvc {
33 | library = null;
34 |
35 | // TODO: dynamically select the header file for the current operating system.
36 | headerFile = "./include/libuvc/libuvc.h";
37 |
38 | header = null;
39 |
40 | async initialize() {
41 | assert.strictEqual(this.library, null);
42 | assert.strictEqual(this.header, null);
43 |
44 | this.library = await load();
45 | const headerLoader = this.library.headers[this.headerFile];
46 | this.header = await headerLoader();
47 |
48 | Object.defineProperties(this, {
49 | constants: {
50 | configurable: true,
51 | get: () => this.header.constants,
52 | },
53 | functions: {
54 | configurable: true,
55 | get: () => this.header.functions,
56 | },
57 | types: {
58 | configurable: true,
59 | get: () => this.header.types,
60 | },
61 | });
62 | }
63 |
64 | async uninitialize() {
65 | assert.notStrictEqual(this.library, null);
66 | assert.notStrictEqual(this.header, null);
67 |
68 | delete this.constants;
69 | delete this.functions;
70 | delete this.types;
71 |
72 | this.header = null;
73 | this.library = null;
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/examples/display-details/display-details.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const Bluebird = require("bluebird");
20 |
21 | const {
22 | Context,
23 | Device,
24 | DeviceDescriptor,
25 | DeviceHandle,
26 | FrameStreamer,
27 | LibUvc,
28 | } = require("../../");
29 |
30 | const main = async () => {
31 | const libuvc = new LibUvc();
32 | await libuvc.initialize();
33 |
34 | const context = new Context(libuvc, null, null);
35 | await context.initialize();
36 |
37 | const devices = context.getDeviceList();
38 | await Bluebird.map(devices, (device) => device.initialize());
39 |
40 | const deviceDescriptors = await Bluebird.map(devices, (device) =>
41 | device.getDescriptor()
42 | );
43 |
44 | await Bluebird.map(deviceDescriptors, (deviceDescriptor) =>
45 | deviceDescriptor.initialize()
46 | );
47 |
48 | console.log(deviceDescriptors);
49 |
50 | // Bluebird.each(deviceDeviceDescriptors, async deviceDeviceDescriptor => {
51 | // const device = new Device(
52 | // libuvc,
53 | // context,
54 | // device.vendor.id,
55 | // device.product.id,
56 | // device.serialNumber,
57 | // libuvc
58 | // );
59 | // await device.initialize();
60 |
61 | // const deviceHandle = new DeviceHandle(libuvc, device);
62 | // await deviceHandle.initialize();
63 |
64 | // await deviceHandle.uninitialize();
65 | // await device.uninitialize();
66 | // });
67 |
68 | await Bluebird.map(deviceDescriptors, (deviceDescriptor) =>
69 | deviceDescriptor.uninitialize()
70 | );
71 | await Bluebird.map(devices, (device) => device.uninitialize());
72 | await context.uninitialize();
73 | await libuvc.uninitialize();
74 | };
75 |
76 | const mainWrapper = async () => {
77 | try {
78 | await main();
79 | } catch (error) {
80 | console.error("main", error);
81 |
82 | process.exitCode = 1;
83 | }
84 | };
85 |
86 | try {
87 | mainWrapper();
88 | } catch (error) {
89 | console.error("mainWrapper", error);
90 |
91 | process.exitCode = 1;
92 | }
93 |
--------------------------------------------------------------------------------
/src/device.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const ref = require("ref-napi");
22 | const libuvc = require("@ffi-libraries/libuvc-v0.0.6");
23 |
24 | const DeviceHandle = require("./device-handle");
25 | const DeviceDescriptor = require("./device-descriptor");
26 |
27 | module.exports = class Device {
28 | pointer = null;
29 | libuvc = null;
30 |
31 | constructor(libuvc, pointer) {
32 | assert(libuvc);
33 | assert(pointer);
34 |
35 | this.libuvc = libuvc;
36 | this.pointer = pointer;
37 | }
38 |
39 | async initialize() {
40 | assert.notStrictEqual(this.libuvc, null);
41 | assert.notStrictEqual(this.pointer, null);
42 | }
43 |
44 | async uninitialize() {
45 | assert.notStrictEqual(this.libuvc, null);
46 | assert.notStrictEqual(this.pointer, null);
47 |
48 | this.libuvc.functions.uvc_unref_device(this.pointer.deref());
49 | this.pointer = null;
50 | }
51 |
52 | async open() {
53 | assert.notStrictEqual(this.libuvc, null);
54 | assert.notStrictEqual(this.pointer, null);
55 |
56 | const deviceHandlePointer = ref.alloc(
57 | this.libuvc.types.uvc_device_handle_tPointer
58 | );
59 |
60 | const result = this.libuvc.functions.uvc_open(
61 | this.pointer.deref(),
62 | deviceHandlePointer
63 | );
64 |
65 | if (result !== 0) {
66 | throw new Error(`this.libuvc.functions.uvc_open(...): ${result}`);
67 | }
68 |
69 | const deviceHandle = new DeviceHandle(this.libuvc, deviceHandlePointer);
70 |
71 | return deviceHandle;
72 | }
73 |
74 | async getDescriptor() {
75 | assert.notStrictEqual(this.libuvc, null);
76 | assert.notStrictEqual(this.pointer, null);
77 |
78 | const deviceDescriptorPointer = ref.alloc(
79 | this.libuvc.types.uvc_device_descriptor_tPointer
80 | );
81 |
82 | const result = this.libuvc.functions.uvc_get_device_descriptor(
83 | this.pointer.deref(),
84 | deviceDescriptorPointer
85 | );
86 |
87 | if (result !== 0) {
88 | throw new Error(
89 | `this.libuvc.functions.uvc_get_device_descriptor(...): ${result}`
90 | );
91 | }
92 |
93 | const deviceDescriptor = new DeviceDescriptor(
94 | this.libuvc,
95 | deviceDescriptorPointer
96 | );
97 |
98 | return deviceDescriptor;
99 | }
100 | };
101 |
--------------------------------------------------------------------------------
/src/device-descriptor.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const ref = require("ref-napi");
22 |
23 | const getPointer = require("./utilities/get-pointer");
24 | const getPointers = require("./utilities/get-pointers");
25 |
26 | module.exports = class DeviceDescriptor {
27 | libuvc = null;
28 | vendorId = null;
29 | vendorName = null;
30 | productId = null;
31 | productName = null;
32 | serialNumber = null;
33 | complianceLevel = null;
34 | pointer = null;
35 |
36 | constructor(libuvc, pointer) {
37 | assert(libuvc);
38 | assert(pointer);
39 |
40 | this.libuvc = libuvc;
41 | this.pointer = pointer;
42 | }
43 |
44 | async initialize() {
45 | assert.notStrictEqual(this.pointer, null);
46 |
47 | const uvcDeviceDescriptor = this.pointer.deref().deref();
48 |
49 | this.vendorId = uvcDeviceDescriptor.idVendor;
50 | this.vendorName = uvcDeviceDescriptor.manufacturer;
51 | this.productId = uvcDeviceDescriptor.idProduct;
52 | this.productName = uvcDeviceDescriptor.product;
53 | this.serialNumber = uvcDeviceDescriptor.serialNumber;
54 | this.complianceLevel = uvcDeviceDescriptor.bcdUVC;
55 | }
56 |
57 | async uninitialize() {
58 | assert.notStrictEqual(this.libuvc, null);
59 | assert.notStrictEqual(this.pointer, null);
60 |
61 | this.vendorId = null;
62 | this.vendorName = null;
63 | this.productId = null;
64 | this.productName = null;
65 | this.serialNumber = null;
66 | this.complianceLevel = null;
67 |
68 | this.libuvc.functions.uvc_free_device_descriptor(this.pointer.deref());
69 |
70 | this.pointer = null;
71 | }
72 |
73 | toString() {
74 | assert.notStrictEqual(this.deviceDescriptor, null);
75 |
76 | return `DeviceDescriptor (vendor 0x${this.vendorId.toString(16)}${
77 | this.vendorName ? `, "${this.vendorName}"` : ""
78 | }, product 0x${this.productId.toString(16)}${
79 | this.productName ? `, "${this.productName}"` : ""
80 | }${this.serialNumber ? `, serial number ${this.serialNumber}` : ""})`;
81 | }
82 |
83 | toJSON(key) {
84 | assert.notStrictEqual(this.deviceDescriptor, null);
85 |
86 | const object = {
87 | vendor: {
88 | id: this.vendorId,
89 | name: this.vendorName,
90 | },
91 | product: {
92 | id: this.productId,
93 | name: this.productName,
94 | },
95 | serialNumber: this.serialNumber,
96 | complianceLevel: this.complianceLevel,
97 | };
98 |
99 | return object;
100 | }
101 | };
102 |
--------------------------------------------------------------------------------
/src/libuvc-standard-units.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 | const fs = require("fs");
21 | const { join } = require("path");
22 | const { promisify } = require("util");
23 |
24 | const { fromPairs, intersection, without, sortBy } = require("lodash");
25 | const YAML = require("yaml");
26 |
27 | const ref = require("ref-napi");
28 | const libuvc = require("@ffi-libraries/libuvc-v0.0.6");
29 |
30 | const assertLibuvcResult = require("./utilities/assert-libuvc-result");
31 | const { DH_CHECK_P_NOT_SAFE_PRIME } = require("constants");
32 |
33 | module.exports = class LibuvcStandardUnits {
34 | standardUnitsYamlPath = null;
35 | license = null;
36 | standardUnits = null;
37 | controls = null;
38 |
39 | constructor(standardUnitsYamlPath) {
40 | this.standardUnitsYamlPath = standardUnitsYamlPath
41 | ? standardUnitsYamlPath
42 | : join(__dirname, "..", "data", "standard-units.yaml");
43 |
44 | this.readFile = promisify(fs.readFile);
45 | }
46 |
47 | async initialize() {
48 | assert.notStrictEqual(this.standardUnitsYamlPath, null);
49 | assert.strictEqual(this.license, null);
50 | assert.strictEqual(this.standardUnits, null);
51 | assert.strictEqual(this.controls, null);
52 |
53 | const standardUnitsYamlFile = await this.readFile(
54 | this.standardUnitsYamlPath
55 | );
56 | const standardUnitsYaml = YAML.parseAllDocuments(
57 | standardUnitsYamlFile.toString()
58 | );
59 |
60 | this.license = Object.freeze(standardUnitsYaml[0].contents.toJSON());
61 | this.standardUnits = Object.freeze(standardUnitsYaml[1].contents.toJSON());
62 |
63 | // TODO: use Object.fromEntries().
64 | this.controls = fromPairs(
65 | sortBy(
66 | Object.entries(this.standardUnits.units)
67 | .map(([unitName, unit]) =>
68 | Object.entries(unit.controls).map(([controlName, control]) => {
69 | const { controls, ...unitWithoutControls } = unit;
70 |
71 | return [
72 | controlName,
73 | {
74 | ...control,
75 | unit: {
76 | ...unitWithoutControls,
77 | },
78 | },
79 | ];
80 | })
81 | )
82 | .reduce(
83 | (controls, unitControls) => controls.concat(unitControls),
84 | []
85 | ),
86 | ([controlName, control]) => controlName
87 | )
88 | );
89 | }
90 |
91 | async uninitialize() {
92 | assert.notStrictEqual(this.license, null);
93 | assert.notStrictEqual(this.standardUnits, null);
94 | assert.notStrictEqual(this.controls, null);
95 |
96 | this.license = null;
97 | this.standardUnits = null;
98 | this.controls = null;
99 | }
100 | };
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Node.js library for USB Video Class (UVC) devices](https://joelpurra.com/projects/node-uvc/) (`node-uvc`)
2 |
3 | Node.js library for [USB Video Class](https://en.wikipedia.org/wiki/USB_video_device_class) (UVC) devices. Used to write software for webcams, camcorders, etcetera.
4 |
5 | [UVC-compliant devices](https://en.wikipedia.org/wiki/List_of_USB_video_class_devices) include webcams, digital camcorders, transcoders, analog video converters and still-image cameras.
6 |
7 | ## Features
8 |
9 | Functional:
10 |
11 | - Locate UVC devices on the system and retrieve out device details.
12 | - Change camera controls, such as image contrast, zoom, and toggling automatic settings.
13 | - Stream video and individual image frames.
14 | - Transform image data using [standard Node.js streams](https://nodejs.org/api/stream.html).
15 |
16 | Technical:
17 |
18 | - Thin translation layer built for Javascript developer convenience.
19 | - Ships with pre-built binaries from [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6).
20 | - Based on the [`libuvc`](https://ken.tossell.net/libuvc/) cross-platform C library.
21 | - Javascript `class` implementation.
22 | - Asynchronous `async`/`await` class methods.
23 | - Resource management using `.initialize()`/`.uninitialize()` methods.
24 |
25 | ## Installation
26 |
27 | Requires [Node.js](https://nodejs.org/) (`node` and `npm` commands). Published on npm as [`uvc`](https://www.npmjs.com/package/uvc).
28 |
29 | ```shell
30 | npm install --save uvc
31 | ```
32 |
33 | ## Usage
34 |
35 | See [`./examples/`](./examples/) for ready-to-run code.
36 |
37 | ```javascript
38 | const { Context, Device, DeviceHandle, LibUvc } = require("uvc");
39 |
40 | const libuvc = new LibUvc();
41 | await libuvc.initialize();
42 |
43 | const context = new Context(libuvc);
44 | await context.initialize();
45 |
46 | const device = await context.findDevice();
47 | await device.initialize();
48 |
49 | const deviceHandle = await device.open();
50 | await deviceHandle.initialize();
51 |
52 | // NOTE: use the UVC device here, for example using the Controls and FrameStreamer classes.
53 |
54 | await deviceHandle.uninitialize();
55 | await device.uninitialize();
56 | await context.uninitialize();
57 | await libuvc.uninitialize();
58 | ```
59 |
60 | ## Development
61 |
62 | - Requires a UVC device, such as a compatible webcam.
63 | - Get the source code from the [`node-uvc` repository](https://github.com/joelpurra/node-uvc).
64 | - Follow [git-flow](https://danielkummer.github.io/git-flow-cheatsheet/) and use [git-flow-avh](https://github.com/petervanderdoes/gitflow-avh).
65 | - Make sure that all example code works by testing them manually.
66 |
67 | ```shell
68 | # Make sure git-flow is initialized.
69 | git flow init -d
70 |
71 | npm run --silent test
72 | ```
73 |
74 | ## See also
75 |
76 | - [`uvcc`](https://joelpurra.com/projects/uvcc) for a command line interface (CLI).
77 | - [USB Video Class](https://en.wikipedia.org/wiki/USB_video_device_class) on Wikipedia.
78 | - [List of USB video class devices](https://en.wikipedia.org/wiki/List_of_USB_video_class_devices) on Wikipedia.
79 | - The [`@ffi-libraries/libuvc-v0.0.6`](https://github.com/node-ffi-libraries/node-ffi-library-libuvc-v0.0.6) Node.js wrapper for [`libuvc`](https://ken.tossell.net/libuvc/).
80 | - The [`v4l-utils`](https://linuxtv.org/wiki/index.php/V4l-utils) for [video4linux](https://www.linuxtv.org) ([Wikipedia](https://en.wikipedia.org/wiki/Video4Linux)), which includes [`v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
81 |
82 | ---
83 |
84 | [`node-uvc`](https://joelpurra.com/projects/node-uvc/) Copyright © 2020, 2021 [Joel Purra](https://joelpurra.com/). Released under [GNU Lesser General Public License version 3.0 (LGPL-3.0)](https://www.gnu.org/licenses/lgpl.html). [Your donations are appreciated!](https://joelpurra.com/donate/)
85 |
--------------------------------------------------------------------------------
/examples/stream-disk/stream-disk.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const { once } = require("events");
20 | const fs = require("fs");
21 | const util = require("util");
22 | const stream = require("stream");
23 |
24 | const {
25 | Context,
26 | Controls,
27 | Device,
28 | DeviceHandle,
29 | FrameStreamer,
30 | LibUvc,
31 | } = require("../../");
32 |
33 | const { Transform } = stream;
34 | const finished = util.promisify(stream.finished);
35 |
36 | const sleep = async (s) =>
37 | new Promise((resolve) => {
38 | setTimeout(resolve, s * 1000);
39 | });
40 |
41 | class FrameImageTransform extends Transform {
42 | constructor() {
43 | super({
44 | writableObjectMode: true,
45 | });
46 | }
47 |
48 | _transform(frame, encoding, callback) {
49 | if (frame.sequence % 30 == 0) {
50 | console.log("* got image", frame.sequence);
51 | }
52 |
53 | return callback(null, frame.image);
54 | }
55 | }
56 |
57 | const main = async () => {
58 | const filename = "stream-disk.mjpeg";
59 |
60 | const libuvc = new LibUvc();
61 | await libuvc.initialize();
62 |
63 | const context = new Context(libuvc);
64 | await context.initialize();
65 |
66 | const device = await context.findDevice();
67 | await device.initialize();
68 |
69 | const deviceHandle = await device.open();
70 | await deviceHandle.initialize();
71 |
72 | const controls = new Controls(libuvc, deviceHandle);
73 | await controls.initialize();
74 |
75 | // https://ken.tossell.net/libuvc/doc/group__ctrl.html#gaa583133ed035c141c42061d5c13a36bf
76 | const UVC_AUTO_EXPOSURE_MODE_AUTO = 2;
77 | const UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY = 8;
78 |
79 | try {
80 | await controls.ae_mode.set(UVC_AUTO_EXPOSURE_MODE_AUTO);
81 | } catch (error) {
82 | if (error.code === "UVC_ERROR_PIPE") {
83 | await controls.ae_mode.set(UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY);
84 | } else {
85 | throw error;
86 | }
87 | }
88 |
89 | const frameStreamer = new FrameStreamer(
90 | libuvc,
91 | deviceHandle,
92 | libuvc.constants.uvc_frame_format.UVC_FRAME_FORMAT_MJPEG,
93 | 640,
94 | 480,
95 | 30
96 | );
97 | const frameStream = await frameStreamer.initialize();
98 |
99 | const frameImageTransform = new FrameImageTransform();
100 | const fileWriteStream = fs.createWriteStream(filename);
101 | frameStream.pipe(frameImageTransform).pipe(fileWriteStream);
102 |
103 | console.log("Streaming...");
104 |
105 | await sleep(10);
106 |
107 | fileWriteStream.end();
108 | await finished(fileWriteStream);
109 | await frameStreamer.uninitialize();
110 | await controls.uninitialize();
111 | await deviceHandle.uninitialize();
112 | await device.uninitialize();
113 | await context.uninitialize();
114 | await libuvc.uninitialize();
115 | };
116 |
117 | const mainWrapper = async () => {
118 | try {
119 | await main();
120 | } catch (error) {
121 | console.error("main", error);
122 |
123 | process.exitCode = 1;
124 | }
125 | };
126 |
127 | try {
128 | mainWrapper();
129 | } catch (error) {
130 | console.error("mainWrapper", error);
131 |
132 | process.exitCode = 1;
133 | }
134 |
--------------------------------------------------------------------------------
/src/context.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const ref = require("ref-napi");
22 |
23 | const Device = require("./device");
24 |
25 | const getPointer = require("./utilities/get-pointer");
26 | const getPointers = require("./utilities/get-pointers");
27 | const { maxNumberOfUvcDevices } = require("./utilities/constants");
28 |
29 | module.exports = class Context {
30 | libuvc = null;
31 | usbContext = null;
32 | uvcContext = null;
33 |
34 | constructor(libuvc, uvcContext = null, usbContext = null) {
35 | assert(libuvc);
36 |
37 | this.libuvc = libuvc;
38 | this.uvcContext = uvcContext;
39 | this.usbContext = usbContext;
40 | }
41 |
42 | async initialize() {
43 | assert.notStrictEqual(this.libuvc, null);
44 |
45 | if (this.uvcContext === null) {
46 | this.uvcContext = ref.alloc(this.libuvc.types.uvc_context_tPointer);
47 | }
48 |
49 | {
50 | const result = this.libuvc.functions.uvc_init(
51 | this.uvcContext,
52 | this.usbContext
53 | );
54 |
55 | if (result !== 0) {
56 | throw new Error(`this.libuvc.functions.uvc_init(...): ${result}`);
57 | }
58 | }
59 | }
60 |
61 | async uninitialize() {
62 | assert.notStrictEqual(this.libuvc, null);
63 | assert.notStrictEqual(this.uvcContext, null);
64 |
65 | this.libuvc.functions.uvc_exit(this.uvcContext.deref());
66 | this.uvcContext = null;
67 | }
68 |
69 | async getDeviceList() {
70 | assert.notStrictEqual(this.context, null);
71 |
72 | // TODO: better ffi-generate support for lists.
73 | const deviceListPointer = Buffer.alloc(
74 | maxNumberOfUvcDevices * ref.types.size_t.size
75 | );
76 |
77 | const result = this.libuvc.functions.uvc_get_device_list(
78 | this.uvcContext.deref(),
79 | deviceListPointer
80 | );
81 |
82 | if (result !== 0) {
83 | throw new Error(
84 | `this.libuvc.functions.uvc_get_device_list(...): ${result}`
85 | );
86 | }
87 |
88 | const pointerToDevicePointer = (pointer) =>
89 | ref.get(pointer, 0, this.libuvc.types.uvc_device_tPointer);
90 |
91 | const devicePointers = getPointers(maxNumberOfUvcDevices, deviceListPointer)
92 | .map(pointerToDevicePointer)
93 | .filter((devicePointer) => !ref.isNull(devicePointer.deref()));
94 |
95 | const devices = devicePointers.map(
96 | (devicePointer) => new Device(this.libuvc, devicePointer)
97 | );
98 |
99 | return devices;
100 | }
101 |
102 | async findDevice(vendorId = 0, productId = 0, serialNumber = null) {
103 | assert.notStrictEqual(this.libuvc, null);
104 | assert.notStrictEqual(this.uvcContext, null);
105 |
106 | const devicePointer = ref.alloc(this.libuvc.types.uvc_device_tPointer);
107 |
108 | const result = this.libuvc.functions.uvc_find_device(
109 | this.uvcContext.deref(),
110 | devicePointer,
111 | vendorId,
112 | productId,
113 | serialNumber
114 | );
115 |
116 | if (result !== 0) {
117 | throw new Error(`this.libuvc.functions.uvc_find_device(...): ${result}`);
118 | }
119 |
120 | const device = new Device(this.libuvc, devicePointer);
121 |
122 | return device;
123 | }
124 | };
125 |
--------------------------------------------------------------------------------
/src/controls.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 |
21 | const { intersection, without } = require("lodash");
22 |
23 | const ref = require("ref-napi");
24 | const libuvc = require("@ffi-libraries/libuvc-v0.0.6");
25 |
26 | const assertLibuvcResult = require("./utilities/assert-libuvc-result");
27 | const LibuvcStandardUnits = require("./libuvc-standard-units");
28 |
29 | module.exports = class Controls {
30 | libuvc = null;
31 | deviceHandle = null;
32 | libuvcStandardUnits = null;
33 |
34 | constructor(libuvc, deviceHandle) {
35 | assert(libuvc);
36 | assert(deviceHandle);
37 |
38 | this.libuvc = libuvc;
39 | this.deviceHandle = deviceHandle;
40 | }
41 |
42 | static getTypeArray(control) {
43 | return Object.entries(control.fields)
44 | .map(
45 | ([name, field]) => `${field.signed ? "" : "u"}int${field.length * 8}`
46 | )
47 | .map((type) => ref.alloc(type));
48 | }
49 |
50 | valueGetterGenerator = (name, control) => () => {
51 | const valueGetter = this.libuvc.functions[`uvc_get_${name}`];
52 | const valueGetterArguments = [
53 | this.deviceHandle.deviceHandle.deref(),
54 | ...Controls.getTypeArray(control),
55 | this.libuvc.constants.uvc_req_code.UVC_GET_CUR,
56 | ];
57 | const result = valueGetter(...valueGetterArguments);
58 |
59 | assertLibuvcResult(
60 | this.libuvc,
61 | result,
62 | `Error getting value for control ${JSON.stringify(name)}.`
63 | );
64 |
65 | const values = valueGetterArguments
66 | .slice(1, -1)
67 | .map((value) => value.deref());
68 |
69 | return values;
70 | };
71 |
72 | valueSetterGenerator = (name) => (value) => {
73 | const valueSetter = this.libuvc.functions[`uvc_set_${name}`];
74 | const setterArguments = [this.deviceHandle.deviceHandle.deref()].concat(
75 | value
76 | );
77 | const result = valueSetter(...setterArguments);
78 |
79 | assertLibuvcResult(
80 | this.libuvc,
81 | result,
82 | `Error setting value ${JSON.stringify(
83 | value
84 | )} for control ${JSON.stringify(name)}.`
85 | );
86 | };
87 |
88 | async initialize() {
89 | assert.notStrictEqual(this.deviceHandle, null);
90 | assert.strictEqual(this.libuvcStandardUnits, null);
91 |
92 | this.libuvcStandardUnits = new LibuvcStandardUnits();
93 | await this.libuvcStandardUnits.initialize();
94 |
95 | for (const [name, control] of Object.entries(
96 | this.libuvcStandardUnits.controls
97 | )) {
98 | const longName = control.control.toLowerCase();
99 |
100 | this[longName] = {
101 | // NOTE: asynchronous getter.
102 | get: async () => this.valueGetterGenerator(name, control)(),
103 | // NOTE: asynchronous setter.
104 | set: async (value) => this.valueSetterGenerator(name)(value),
105 |
106 | // TODO: fix ffi-generate to output the uvc_ct_ctrl_selector constants, then use them for uvc_get_ctrl(...).
107 | // TODO: move/copy uvc_ct_ctrl_selector to standard-units.yaml?
108 | // TODO: use uvc_ct_ctrl_selector for the "regular" control value getters/setters?
109 | // min: minGetterGenerator(name),
110 | // max: maxGetterGenerator(name),
111 | // def: defGetterGenerator(name),
112 | // info: infoGetterGenerator(name)
113 | };
114 | }
115 |
116 | // TODO: seal self to avoid accidental controls.typo = 999;
117 | Object.seal(this);
118 | }
119 |
120 | async uninitialize() {
121 | assert.notStrictEqual(this.libuvc, null);
122 | assert.notStrictEqual(this.deviceHandle, null);
123 | assert.notStrictEqual(this.libuvcStandardUnits, null);
124 |
125 | await this.libuvcStandardUnits.uninitialize();
126 | this.libuvcStandardUnits = null;
127 | }
128 | };
129 |
--------------------------------------------------------------------------------
/src/frame-streamer.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of node-uvc -- Library for USB Video Class (UVC) devices.
3 | Copyright (C) 2020 Joel Purra
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | const assert = require("assert");
20 | const stream = require("stream");
21 | const util = require("util");
22 |
23 | const ffi = require("ffi-napi");
24 | const ref = require("ref-napi");
25 | const libuvc = require("@ffi-libraries/libuvc-v0.0.6");
26 |
27 | const FrameStream = require("./frame-stream");
28 |
29 | const finished = util.promisify(stream.finished);
30 | const js_void = ref.types.void;
31 | const js_voidPointer = ref.refType(js_void);
32 |
33 | module.exports = class FrameStreamer {
34 | libuvc = null;
35 | deviceHandle = null;
36 | format = null;
37 | width = 0;
38 | height = 0;
39 | fps = 0;
40 | deviceHandle = null;
41 | frameStream = null;
42 | onFrameCallback = null;
43 |
44 | constructor(libuvc, deviceHandle, format, width, height, fps) {
45 | assert(libuvc);
46 | assert(deviceHandle);
47 | assert(format > 0);
48 | assert(width > 0);
49 | assert(height > 0);
50 | assert(fps > 0);
51 |
52 | this.libuvc = libuvc;
53 | this.deviceHandle = deviceHandle;
54 | this.format = format;
55 | this.width = width;
56 | this.height = height;
57 | this.fps = fps;
58 | }
59 |
60 | async initialize() {
61 | assert.notStrictEqual(this.libuvc, null);
62 | assert.notStrictEqual(this.deviceHandle, null);
63 | assert.strictEqual(this.frameStream, null);
64 | assert.strictEqual(this.onFrameCallback, null);
65 |
66 | const controlBlockFormatSize = ref.alloc(
67 | this.libuvc.types.uvc_stream_ctrl_t
68 | );
69 |
70 | {
71 | const result = this.libuvc.functions.uvc_get_stream_ctrl_format_size(
72 | this.deviceHandle.deviceHandle.deref(),
73 | controlBlockFormatSize,
74 | this.format,
75 | this.width,
76 | this.height,
77 | this.fps
78 | );
79 |
80 | if (result !== 0) {
81 | throw new Error(
82 | `this.libuvc.functions.uvc_get_stream_ctrl_format_size(...): ${result}`
83 | );
84 | }
85 | }
86 |
87 | {
88 | this.frameStream = new FrameStream();
89 | this.onFrameCallback = ffi.Callback(
90 | "void",
91 | [this.libuvc.types.uvc_frame_tPointer, js_voidPointer],
92 | (frame, userPointer) => this.frameStream.write(frame)
93 | );
94 |
95 | // NOTE: keeping state in the class rather than in the user pointer.
96 | // Creation: const userPointer = ref.alloc("Object", anything);
97 | // Consumption in callback: const anything = ref.readObject(userPointer, 0);
98 | const userPointer = ref.NULL_POINTER;
99 |
100 | // NOTE: Stream setup flags, currently undefined. Set this to zero. The lower bit is reserved for backward compatibility.
101 | // https://ken.tossell.net/libuvc/doc/group__streaming.html#gaa7edf40956feeca14794f6cd6462e316
102 | const streamSetupFlags = 0;
103 |
104 | const result = this.libuvc.functions.uvc_start_streaming(
105 | this.deviceHandle.deviceHandle.deref(),
106 | controlBlockFormatSize,
107 | this.onFrameCallback,
108 | userPointer,
109 | streamSetupFlags
110 | );
111 |
112 | if (result !== 0) {
113 | throw new Error(
114 | `this.libuvc.functions.uvc_start_streaming(...): ${result}`
115 | );
116 | }
117 | }
118 |
119 | return this.frameStream;
120 | }
121 |
122 | async uninitialize() {
123 | assert.notStrictEqual(this.libuvc, null);
124 | assert.notStrictEqual(this.deviceHandle, null);
125 | assert.notStrictEqual(this.onFrameCallback, null);
126 | assert.notStrictEqual(this.frameStream, null);
127 |
128 | this.libuvc.functions.uvc_stop_streaming(
129 | this.deviceHandle.deviceHandle.deref()
130 | );
131 |
132 | this.onFrameCallback = null;
133 |
134 | this.frameStream.end();
135 |
136 | // TODO: figure out why the application crashes silently when waiting to finish the writable part of the transform stream.
137 | await finished(this.frameStream, {
138 | writable: false,
139 | });
140 |
141 | this.frameStream = null;
142 | }
143 | };
144 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/examples/reference-implementation/stream-disk/stream-disk.js:
--------------------------------------------------------------------------------
1 | /* Software License Agreement (BSD License)
2 | *
3 | * Copyright (C) 2010-2015 Ken Tossell
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * * Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * * Redistributions in binary form must reproduce the above
13 | * copyright notice, this list of conditions and the following
14 | * disclaimer in the documentation and/or other materials provided
15 | * with the distribution.
16 | * * Neither the name of the author nor other contributors may be
17 | * used to endorse or promote products derived from this software
18 | * without specific prior written permission.
19 | *
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 | * POSSIBILITY OF SUCH DAMAGE.
32 | */
33 |
34 | // #include "libuvc/libuvc.h"
35 | // #include
36 |
37 | const { once } = require("events");
38 | const fs = require("fs");
39 | const util = require("util");
40 | const stream = require("stream");
41 |
42 | const ffi = require("ffi-napi");
43 | const ref = require("ref-napi");
44 |
45 | const { load } = require("@ffi-libraries/libuvc-v0.0.6");
46 |
47 | const finished = util.promisify(stream.finished);
48 | const js_void = ref.types.void;
49 | const js_voidPointer = ref.refType(js_void);
50 |
51 | const sleep = async (s) =>
52 | new Promise((resolve) => {
53 | setTimeout(resolve, s * 1000);
54 | });
55 |
56 | /* This callback function runs once per frame. Use it to perform any
57 | * quick processing you need, or have it put the frame into your application's
58 | * input queue. If this function takes too long, you'll start losing frames. */
59 | async function /* void */ cb(/* uvc_frame_t * */ frame, /* void * */ ptr) {
60 | const fileWriteStream = ref.readObject(ptr, 0);
61 | const frameDeref = frame.deref();
62 | const mjpeg = ref.reinterpret(frameDeref.data, frameDeref.data_bytes, 0);
63 |
64 | if (!fileWriteStream.write(mjpeg)) {
65 | await once(fileWriteStream, "drain");
66 | }
67 |
68 | if (frameDeref.sequence % 30 == 0) {
69 | console.log(" * got image", frameDeref.sequence);
70 | }
71 | }
72 |
73 | async function /* int */ main(/* int */ argc, /* char ** */ argv) {
74 | const library = await load();
75 | const headerLoader = library.headers["./include/libuvc/libuvc.h"];
76 | const libuvc = await headerLoader();
77 |
78 | const filename = "stream-disk.mjpeg";
79 | const /* uvc_context_t * */ ctx = ref.alloc(
80 | libuvc.types.uvc_context_tPointer
81 | );
82 | const /* uvc_device_t * */ dev = ref.alloc(libuvc.types.uvc_device_tPointer);
83 | const /* uvc_device_handle_t * */ devh = ref.alloc(
84 | libuvc.types.uvc_device_handle_tPointer
85 | );
86 | const /* uvc_stream_ctrl_t */ ctrl = ref.alloc(
87 | libuvc.types.uvc_stream_ctrl_t
88 | );
89 | let /* uvc_error_t */ res;
90 |
91 | /* Initialize a UVC service context. Libuvc will set up its own libusb
92 | * context. Replace NULL with a libusb_context pointer to run libuvc
93 | * from an existing libusb context. */
94 | res = libuvc.functions.uvc_init(/* & */ ctx, null);
95 |
96 | if (res < 0) {
97 | libuvc.functions.uvc_perror(res, "uvc_init");
98 | return res;
99 | }
100 |
101 | console.log("UVC initialized");
102 |
103 | /* Locates the first attached UVC device, stores in dev */
104 | res = libuvc.functions.uvc_find_device(
105 | ctx.deref(),
106 | /* & */ dev,
107 | 0,
108 | 0,
109 | null
110 | ); /* filter devices: vendor_id, product_id, "serial_num" */
111 |
112 | if (res < 0) {
113 | libuvc.functions.uvc_perror(res, "uvc_find_device"); /* no devices found */
114 | } else {
115 | console.log("Device found");
116 |
117 | /* Try to open the device: requires exclusive access */
118 | res = libuvc.functions.uvc_open(dev.deref(), /* & */ devh);
119 |
120 | if (res < 0) {
121 | libuvc.functions.uvc_perror(res, "uvc_open"); /* unable to open device */
122 | } else {
123 | console.log("Device opened");
124 |
125 | /* Print out a message containing all the information that libuvc
126 | * knows about the device */
127 | libuvc.functions.uvc_print_diag(devh.deref(), null);
128 |
129 | /* Try to negotiate a 640x480 30 fps YUYV stream profile */
130 | res = libuvc.functions.uvc_get_stream_ctrl_format_size(
131 | devh.deref(),
132 | /* & */ ctrl /* result stored in ctrl */,
133 | libuvc.constants.uvc_frame_format
134 | .UVC_FRAME_FORMAT_MJPEG /* MJPEG for writing to disk */,
135 | 640,
136 | 480,
137 | 30 /* width, height, fps */
138 | );
139 |
140 | /* Print out the result */
141 | libuvc.functions.uvc_print_stream_ctrl(/* & */ ctrl, null);
142 |
143 | if (res < 0) {
144 | libuvc.functions.uvc_perror(
145 | res,
146 | "get_mode"
147 | ); /* device doesn't provide a matching stream */
148 | } else {
149 | /* Start the video stream. The library will call user function cb:
150 | * cb(frame, (void*) user_ptr)
151 | */
152 | const callback = ffi.Callback(
153 | "void",
154 | [libuvc.types.uvc_frame_tPointer, js_voidPointer],
155 | cb
156 | );
157 | const fileWriteStream = fs.createWriteStream(filename);
158 | const user_ptr = ref.alloc("Object", fileWriteStream);
159 | res = libuvc.functions.uvc_start_streaming(
160 | devh.deref(),
161 | /* & */ ctrl,
162 | callback,
163 | user_ptr,
164 | 0
165 | );
166 |
167 | if (res < 0) {
168 | fileWriteStream.end();
169 | await finished(fileWriteStream);
170 | libuvc.functions.uvc_perror(
171 | res,
172 | "start_streaming"
173 | ); /* unable to start stream */
174 | } else {
175 | console.log("Streaming...");
176 |
177 | /* enable auto exposure - see uvc_set_ae_mode documentation */
178 | console.log("Enabling auto exposure ...");
179 |
180 | const /* uint8_t */ UVC_AUTO_EXPOSURE_MODE_AUTO = 2;
181 | res = libuvc.functions.uvc_set_ae_mode(
182 | devh.deref(),
183 | UVC_AUTO_EXPOSURE_MODE_AUTO
184 | );
185 |
186 | if (res == libuvc.constants.uvc_error.UVC_SUCCESS) {
187 | console.log(" ... enabled auto exposure");
188 | } else if (res == libuvc.constants.uvc_error.UVC_ERROR_PIPE) {
189 | /* this error indicates that the camera does not support the full AE mode;
190 | * try again, using aperture priority mode (fixed aperture, variable exposure time) */
191 | console.log(
192 | " ... full AE not supported, trying aperture priority mode"
193 | );
194 | const /* uint8_t */ UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY = 8;
195 | res = libuvc.functions.uvc_set_ae_mode(
196 | devh.deref(),
197 | UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY
198 | );
199 |
200 | if (res < 0) {
201 | libuvc.functions.uvc_perror(
202 | res,
203 | " ... uvc_set_ae_mode failed to enable aperture priority mode"
204 | );
205 | } else {
206 | console.log(" ... enabled aperture priority auto exposure mode");
207 | }
208 | } else {
209 | libuvc.functions.uvc_perror(
210 | res,
211 | " ... uvc_set_ae_mode failed to enable auto exposure mode"
212 | );
213 | }
214 |
215 | await sleep(10); /* stream for 10 seconds */
216 |
217 | fileWriteStream.end();
218 | await finished(fileWriteStream);
219 |
220 | /* End the stream. Blocks until last callback is serviced */
221 | libuvc.functions.uvc_stop_streaming(devh.deref());
222 | console.log("Done streaming.");
223 | }
224 | }
225 |
226 | /* Release our handle on the device */
227 | libuvc.functions.uvc_close(devh.deref());
228 | console.log("Device closed");
229 | }
230 |
231 | /* Release the device descriptor */
232 | libuvc.functions.uvc_unref_device(dev.deref());
233 | }
234 |
235 | /* Close the UVC context. This closes and cleans up any existing device handles,
236 | * and it closes the libusb context if one was not provided. */
237 | libuvc.functions.uvc_exit(ctx.deref());
238 |
239 | await library.unload();
240 |
241 | console.log("UVC exited");
242 |
243 | return 0;
244 | }
245 |
246 | async function mainWrapper() {
247 | try {
248 | await main();
249 | } catch (error) {
250 | console.log("main", error);
251 |
252 | process.exitCode = 1;
253 | }
254 | }
255 |
256 | try {
257 | mainWrapper();
258 | } catch (error) {
259 | console.error("mainWrapper", error);
260 |
261 | process.exitCode = 1;
262 | }
263 |
--------------------------------------------------------------------------------
/data/standard-units.yaml:
--------------------------------------------------------------------------------
1 | # LICENSE.txt
2 | --- |
3 | Software License Agreement (BSD License)
4 |
5 | Copyright (C) 2010-2015 Ken Tossell
6 | All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions
10 | are met:
11 |
12 | * Redistributions of source code must retain the above copyright
13 | notice, this list of conditions and the following disclaimer.
14 | * Redistributions in binary form must reproduce the above
15 | copyright notice, this list of conditions and the following
16 | disclaimer in the documentation and/or other materials provided
17 | with the distribution.
18 | * Neither the name of the author nor other contributors may be
19 | used to endorse or promote products derived from this software
20 | without specific prior written permission.
21 |
22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 | POSSIBILITY OF SUCH DAMAGE.
34 |
35 | # standard-units.yaml
36 | ---
37 | units:
38 | camera_terminal:
39 | type: standard
40 | description: Standard camera input terminal (captures images from sensor)
41 | control_prefix: CT
42 | controls:
43 | scanning_mode:
44 | control: SCANNING_MODE
45 | length: 1
46 | fields:
47 | mode:
48 | type: int
49 | position: 0
50 | length: 1
51 | doc: "0: interlaced, 1: progressive"
52 | ae_mode:
53 | control: AE_MODE
54 | length: 1
55 | fields:
56 | mode:
57 | type: int
58 | position: 0
59 | length: 1
60 | doc:
61 | "1: manual mode; 2: auto mode; 4: shutter priority mode; 8: aperture
62 | priority mode"
63 | doc:
64 | get: |-
65 | @brief Reads camera's auto-exposure mode.
66 |
67 | See uvc_set_ae_mode() for a description of the available modes.
68 | set: |-
69 | @brief Sets camera's auto-exposure mode.
70 |
71 | Cameras may support any of the following AE modes:
72 | * UVC_AUTO_EXPOSURE_MODE_MANUAL (1) - manual exposure time, manual iris
73 | * UVC_AUTO_EXPOSURE_MODE_AUTO (2) - auto exposure time, auto iris
74 | * UVC_AUTO_EXPOSURE_MODE_SHUTTER_PRIORITY (4) - manual exposure time, auto iris
75 | * UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY (8) - auto exposure time, manual iris
76 |
77 | Most cameras provide manual mode and aperture priority mode.
78 | ae_priority:
79 | control: AE_PRIORITY
80 | length: 1
81 | fields:
82 | priority:
83 | type: int
84 | position: 0
85 | length: 1
86 | doc:
87 | "0: frame rate must remain constant; 1: frame rate may be varied
88 | for AE purposes"
89 | doc:
90 | get: |-
91 | @brief Checks whether the camera may vary the frame rate for exposure control reasons.
92 | See uvc_set_ae_priority() for a description of the `priority` field.
93 | set: |-
94 | @brief Chooses whether the camera may vary the frame rate for exposure control reasons.
95 | A `priority` value of zero means the camera may not vary its frame rate. A value of 1
96 | means the frame rate is variable. This setting has no effect outside of the `auto` and
97 | `shutter_priority` auto-exposure modes.
98 | exposure_abs:
99 | control: EXPOSURE_TIME_ABSOLUTE
100 | length: 4
101 | fields:
102 | time:
103 | type: int
104 | position: 0
105 | length: 4
106 | doc: ""
107 | doc:
108 | get: |-
109 | @brief Gets the absolute exposure time.
110 |
111 | See uvc_set_exposure_abs() for a description of the `time` field.
112 | set: |-
113 | @brief Sets the absolute exposure time.
114 |
115 | The `time` parameter should be provided in units of 0.0001 seconds (e.g., use the value 100
116 | for a 10ms exposure period). Auto exposure should be set to `manual` or `shutter_priority`
117 | before attempting to change this setting.
118 | exposure_rel:
119 | control: EXPOSURE_TIME_RELATIVE
120 | length: 1
121 | fields:
122 | step:
123 | type: int
124 | position: 0
125 | length: 1
126 | signed: true
127 | doc:
128 | number of steps by which to change the exposure time, or zero to
129 | set the default exposure time
130 | doc: "@brief {gets_sets} the exposure time relative to the current setting."
131 | focus_abs:
132 | control: FOCUS_ABSOLUTE
133 | length: 2
134 | fields:
135 | focus:
136 | type: int
137 | position: 0
138 | length: 2
139 | doc: focal target distance in millimeters
140 | doc: "@brief {gets_sets} the distance at which an object is optimally focused."
141 | focus_rel:
142 | control: FOCUS_RELATIVE
143 | length: 2
144 | fields:
145 | focus_rel:
146 | type: int
147 | position: 0
148 | length: 1
149 | signed: true
150 | doc: TODO
151 | speed:
152 | type: int
153 | position: 1
154 | length: 1
155 | doc: TODO
156 | focus_simple_range:
157 | control: FOCUS_SIMPLE
158 | length: 1
159 | fields:
160 | focus:
161 | type: int
162 | position: 0
163 | length: 1
164 | doc: TODO
165 | focus_auto:
166 | control: FOCUS_AUTO
167 | length: 1
168 | fields:
169 | state:
170 | type: int
171 | position: 0
172 | length: 1
173 | doc: TODO
174 | iris_abs:
175 | control: IRIS_ABSOLUTE
176 | length: 2
177 | fields:
178 | iris:
179 | type: int
180 | position: 0
181 | length: 2
182 | doc: TODO
183 | iris_rel:
184 | control: IRIS_RELATIVE
185 | length: 1
186 | fields:
187 | iris_rel:
188 | type: int
189 | position: 0
190 | length: 1
191 | doc: TODO
192 | zoom_abs:
193 | control: ZOOM_ABSOLUTE
194 | length: 2
195 | fields:
196 | focal_length:
197 | type: int
198 | position: 0
199 | length: 2
200 | doc: TODO
201 | zoom_rel:
202 | control: ZOOM_RELATIVE
203 | length: 3
204 | fields:
205 | zoom_rel:
206 | type: int
207 | position: 0
208 | length: 1
209 | signed: true
210 | doc: TODO
211 | digital_zoom:
212 | type: int
213 | position: 1
214 | length: 1
215 | doc: TODO
216 | speed:
217 | type: int
218 | position: 2
219 | length: 1
220 | doc: TODO
221 | pantilt_abs:
222 | control: PANTILT_ABSOLUTE
223 | length: 8
224 | fields:
225 | pan:
226 | type: int
227 | position: 0
228 | length: 4
229 | signed: true
230 | doc: TODO
231 | tilt:
232 | type: int
233 | position: 4
234 | length: 4
235 | signed: true
236 | doc: TODO
237 | pantilt_rel:
238 | control: PANTILT_RELATIVE
239 | length: 4
240 | fields:
241 | pan_rel:
242 | type: int
243 | position: 0
244 | length: 1
245 | signed: true
246 | doc: TODO
247 | pan_speed:
248 | type: int
249 | position: 1
250 | length: 1
251 | doc: TODO
252 | tilt_rel:
253 | type: int
254 | position: 2
255 | length: 1
256 | signed: true
257 | doc: TODO
258 | tilt_speed:
259 | type: int
260 | position: 3
261 | length: 1
262 | doc: TODO
263 | roll_abs:
264 | control: ROLL_ABSOLUTE
265 | length: 2
266 | fields:
267 | roll:
268 | type: int
269 | position: 0
270 | length: 2
271 | signed: true
272 | doc: TODO
273 | roll_rel:
274 | control: ROLL_RELATIVE
275 | length: 2
276 | fields:
277 | roll_rel:
278 | type: int
279 | position: 0
280 | length: 1
281 | signed: true
282 | doc: TODO
283 | speed:
284 | type: int
285 | position: 1
286 | length: 1
287 | doc: TODO
288 | privacy:
289 | control: PRIVACY
290 | length: 1
291 | fields:
292 | privacy:
293 | type: int
294 | position: 0
295 | length: 1
296 | doc: TODO
297 | digital_window:
298 | control: DIGITAL_WINDOW
299 | length: 12
300 | fields:
301 | window_top:
302 | type: int
303 | position: 0
304 | length: 2
305 | doc: TODO
306 | window_left:
307 | type: int
308 | position: 2
309 | length: 2
310 | doc: TODO
311 | window_bottom:
312 | type: int
313 | position: 4
314 | length: 2
315 | doc: TODO
316 | window_right:
317 | type: int
318 | position: 6
319 | length: 2
320 | doc: TODO
321 | num_steps:
322 | type: int
323 | position: 8
324 | length: 2
325 | doc: TODO
326 | num_steps_units:
327 | type: int
328 | position: 10
329 | length: 2
330 | doc: TODO
331 | digital_roi:
332 | control: REGION_OF_INTEREST
333 | length: 10
334 | fields:
335 | roi_top:
336 | type: int
337 | position: 0
338 | length: 2
339 | doc: TODO
340 | roi_left:
341 | type: int
342 | position: 2
343 | length: 2
344 | doc: TODO
345 | roi_bottom:
346 | type: int
347 | position: 4
348 | length: 2
349 | doc: TODO
350 | roi_right:
351 | type: int
352 | position: 6
353 | length: 2
354 | doc: TODO
355 | auto_controls:
356 | type: int
357 | position: 8
358 | length: 2
359 | doc: TODO
360 | processing_unit:
361 | type: standard
362 | description: Standard processing unit (processes images between other units)
363 | control_prefix: PU
364 | controls:
365 | backlight_compensation:
366 | control: BACKLIGHT_COMPENSATION
367 | length: 2
368 | fields:
369 | backlight_compensation:
370 | type: int
371 | position: 0
372 | length: 2
373 | doc:
374 | device-dependent backlight compensation mode; zero means backlight
375 | compensation is disabled
376 | brightness:
377 | control: BRIGHTNESS
378 | length: 2
379 | fields:
380 | brightness:
381 | type: int
382 | position: 0
383 | length: 2
384 | signed: true
385 | doc: TODO
386 | contrast:
387 | control: CONTRAST
388 | length: 2
389 | fields:
390 | contrast:
391 | type: int
392 | position: 0
393 | length: 2
394 | doc: TODO
395 | contrast_auto:
396 | control: CONTRAST_AUTO
397 | length: 1
398 | fields:
399 | contrast_auto:
400 | type: int
401 | position: 0
402 | length: 1
403 | doc: TODO
404 | gain:
405 | control: GAIN
406 | length: 2
407 | fields:
408 | gain:
409 | type: int
410 | position: 0
411 | length: 2
412 | doc: TODO
413 | power_line_frequency:
414 | control: POWER_LINE_FREQUENCY
415 | length: 1
416 | fields:
417 | power_line_frequency:
418 | type: int
419 | position: 0
420 | length: 1
421 | doc: TODO
422 | hue:
423 | control: HUE
424 | length: 2
425 | fields:
426 | hue:
427 | type: int
428 | position: 0
429 | length: 2
430 | signed: true
431 | doc: TODO
432 | hue_auto:
433 | control: HUE_AUTO
434 | length: 1
435 | fields:
436 | hue_auto:
437 | type: int
438 | position: 0
439 | length: 1
440 | doc: TODO
441 | saturation:
442 | control: SATURATION
443 | length: 2
444 | fields:
445 | saturation:
446 | type: int
447 | position: 0
448 | length: 2
449 | doc: TODO
450 | sharpness:
451 | control: SHARPNESS
452 | length: 2
453 | fields:
454 | sharpness:
455 | type: int
456 | position: 0
457 | length: 2
458 | doc: TODO
459 | gamma:
460 | control: GAMMA
461 | length: 2
462 | fields:
463 | gamma:
464 | type: int
465 | position: 0
466 | length: 2
467 | doc: TODO
468 | white_balance_temperature:
469 | control: WHITE_BALANCE_TEMPERATURE
470 | length: 2
471 | fields:
472 | temperature:
473 | type: int
474 | position: 0
475 | length: 2
476 | doc: TODO
477 | white_balance_temperature_auto:
478 | control: WHITE_BALANCE_TEMPERATURE_AUTO
479 | length: 1
480 | fields:
481 | temperature_auto:
482 | type: int
483 | position: 0
484 | length: 1
485 | doc: TODO
486 | white_balance_component:
487 | control: WHITE_BALANCE_COMPONENT
488 | length: 4
489 | fields:
490 | blue:
491 | type: int
492 | position: 0
493 | length: 2
494 | doc: TODO
495 | red:
496 | type: int
497 | position: 2
498 | length: 2
499 | doc: TODO
500 | white_balance_component_auto:
501 | control: WHITE_BALANCE_COMPONENT_AUTO
502 | length: 1
503 | fields:
504 | white_balance_component_auto:
505 | type: int
506 | position: 0
507 | length: 1
508 | doc: TODO
509 | digital_multiplier:
510 | control: DIGITAL_MULTIPLIER
511 | length: 2
512 | fields:
513 | multiplier_step:
514 | type: int
515 | position: 0
516 | length: 2
517 | doc: TODO
518 | digital_multiplier_limit:
519 | control: DIGITAL_MULTIPLIER_LIMIT
520 | length: 2
521 | fields:
522 | multiplier_step:
523 | type: int
524 | position: 0
525 | length: 2
526 | doc: TODO
527 | analog_video_standard:
528 | control: ANALOG_VIDEO_STANDARD
529 | length: 1
530 | fields:
531 | video_standard:
532 | type: int
533 | position: 0
534 | length: 1
535 | doc: TODO
536 | analog_video_lock_status:
537 | control: ANALOG_LOCK_STATUS
538 | length: 1
539 | fields:
540 | status:
541 | type: int
542 | position: 0
543 | length: 1
544 | doc: TODO
545 | selector_unit:
546 | type: standard
547 | description: Standard selector unit (controls connectivity between other units)
548 | control_prefix: SU
549 | controls:
550 | input_select:
551 | control: INPUT_SELECT
552 | length: 1
553 | fields:
554 | selector:
555 | type: int
556 | position: 0
557 | length: 1
558 | doc: TODO
559 |
560 |
--------------------------------------------------------------------------------