joelpurra / node-uvc

Node.js library for USB Video Class (UVC) devices. Used to write software for webcams, camcorders, etcetera.
https://joelpurra.com/projects/node-uvc/
GNU Lesser General Public License v3.0
17 stars 1 forks source link

UVC_ERROR_ACCESS on macOS Monterey and Ubuntu (on Raspberry Pi 4) #2

Open pandres95 opened 2 years ago

pandres95 commented 2 years ago

I'm trying to use uvc@1.0.0 under the following environment:

OS: macOS Monterey (version 12.1 (21C52)), Node version(s): v14.18.3, v16.13.2 libuvc version: 0.0.6.

This is the code I'm using so far to test it:

/**
 *
 * @param {import('../src').SendVideoSource} sendVideoSource
 */
async function webcamUVCSignalTest(sendVideoSource) {
    const libuvc = new LibUvc();
    await libuvc.initialize();

    const context = new Context(libuvc);
    await context.initialize();

    const devices = await context.getDeviceList();

    if (!devices.length) {
        return;
    }

    const { device } = await prompts({
        type: 'select',
        name: 'device',
        choices: await Promise.all(devices.map(async device => ({
            title: await device.getDescriptor().then(async d => {
                await d.initialize();
                return d.productName;
            }),
            value: device
        }))),
        message: 'Select the device:'
    });

    await device.initialize();

    const deviceHandle = await device.open(); // <--------- The library fails here
    await deviceHandle.initialize();

    const controls = new Controls(libuvc, deviceHandle);
    await controls.initialize();

    const UVC_AUTO_EXPOSURE_MODE_AUTO = 2;
    const UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY = 8;

    try {
        await controls.ae_mode.set(UVC_AUTO_EXPOSURE_MODE_AUTO);
    } catch (error) {
        if (error.code === "UVC_ERROR_PIPE") {
            await controls.ae_mode.set(UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY);
        } else {
            throw error;
        }
    }

    const frameStreamer = new FrameStreamer(
        libuvc,
        deviceHandle,
        libuvc.constants.uvc_frame_format.UVC_FRAME_FORMAT_MJPEG,
        1280,
        720,
        30
    );
    const frameStream = await frameStreamer.initialize();

    frameStream.pipe(callback((error, data) => {
        if (error) {
            return;
        }

        debugger;
    }))

}

The error I'm getting in the output is the following UVC_ERROR_ACCESS:

Error: this.libuvc.functions.uvc_open(...): -3
    at Device.open (/Users/pandres95/Documents/Development/OSS/node-ndi/bin/node_modules/uvc/src/device.js:67:13)
    at webcamUVCSignalTest (file:///Users/pandres95/Documents/Development/OSS/node-ndi/bin/ndi.js:174:39)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Screenshot of the complete output message:

Screenshot 2022-01-16 at 3 39 44 PM

Is there something I might be doing wrong, or even something unrelated to my code but related to an issue with how Node accesses to libuvc, maybe?

joelpurra commented 2 years ago

@pandres95: that's unfortunate. Am not using macOS 12, so can't debug myself.

My first guess would be that the OS driver has claimed the camera device, and is not letting it go. A quick search in the upstream libuvc+libusb issues seems to lead in the same direction. Here are links to some related issues; I didn't read all the comments carefully.

This is clearly related to a change in macOS 12. While it might be possible to solve, libuvc might not be the right tool for this as it expects exclusive device access to capture the USB data stream. It'd be better to use the macOS 12 system media libraries to interface with the UVC camera, which may also allow other applications to do the same by leaving data brokering to the system media drivers.

Does that help? Could you post the solution you end up using?


I see that you are working on a Network Device Interface (NDI) video streaming library for Node.js. Is the goal to support UVC devices as NDI devices? If so, cool -- might be useful for what I'm working on too =)

While libuvc/libusb would (or used to?) be suitable for cross-platform development, the direction operating systems are taking is making this harder. I guess good audio+video support is becoming more important to users, so operating systems take more control. See also a recent discussion regarding libuvc/libusb on Windows.

pandres95 commented 2 years ago

Yeah! It's actually pretty useful to know this is a recurring issue. Since I hadn't had time to try reproducing it on other devices (such a Raspberry Pi), I was stuck thinking it was an issue with the library. That was until finding out what the -3 error meant, and started getting deeper into the issues.

Actually, after reading these issues, might consider skipping tests over macOS 12+ for now, at least until a better solution for camera usage on macOS (other solutions such as node-webcam or camera-capture work horribly —either by exhausting memory resources, or having a serious low framerate—, and that's why I considered using uvc instead.

Actually, my intent with using uvc was to have some sort of testing codebase to use with the NDI library. Since the approach when writing it was to be as clean as possible, no decoding capabilities are added, or even support for external devices (such as the camera) are supported by default. However, I thought it would be an awesome idea to have some working code to serve as an example. And that's what brought me here. 😅


Also, it's really cool to know you might be interested in the NDI library. 😁 It'll take me at least a couple of weeks to have it working with audio, and both video/audio, so that first working version would be complete.

That said, once I have it, I'll let you know.

pandres95 commented 2 years ago

Hi @joelpurra!

Just by chance I was checking the usage of the library on the Raspberry Pi 4, using Ubuntu 21.10. This is because well… it's the use case why I'm building the NDI library in first place 😅 . I found out two things:

  1. Unfortunately there are no built binaries of the @node-ffi-libraries for the platform linux-armv8, which I worked around by initially disabling the UVC library at all on my sample file. Then, I just installed the according latest dependencies available on universal arm64 repositories for Ubuntu (the packages I installed were libuvc-dev, libusb-1.0.0-dev, and libjpeg9-dev respectively).

  2. Found out that the test device I used there wasn't properly initializing the descriptors as you can see in the screenshot below. Not sure if that's because of the slightly different versions (i.e. having libusb 1.0.24 instead of 1.0.23), or something related to the armv8 (a.k.a. arm64) arch managing pointers differently (actually, I've found related issues on my own library hehe). Screenshot 2022-01-23 at 4 10 40 PM Screenshot 2022-01-23 at 4 10 57 PM

  3. Well… back to our main issue, I ended up getting the -3 error… again. 😅 Screenshot 2022-01-23 at 4 25 03 PM

The environment to try reproducing the issue is the following:

OS: Ubuntu Desktop 21.10, Node version(s): v14.18.3, v16.13.2 libuvc version: 0.0.6. libusb version: 1.0.24. libjpeg version: 9.

joelpurra commented 2 years ago

@pandres95:

  1. Great that the workaround works! The missing platform support because of the libuvc/libjpeg/libusb recipes in Conan Center, which does not support arm (and variants) for all versions and combinations. Since you have confirmed that the libraries works on arm64, perhaps you can help/request Conan Center to build them? They might not prioritize such builds unless specifically asked. When the recipe supports the platform, new releases of the libraries can be generated. You can also use/test generation of libraries locally, even though it's not trivial when also dealing with dependencies.
  2. Until macOS 12, users were conveniently allowed to access webcams freely. In Linux this is not the (default) case. Did you set up user access to the device? (Using root access is a workaround.)

By the way, the missing descriptor issue has popped up for macOS users in other projects (https://github.com/joelpurra/uvcc/issues/23), presumably due to macOS 12 issues. The underlying issue seems to be the same; USB/webcam access is now restricted.

mcuee commented 2 years ago

Workaround which may not be easy to implement (using a codeless DriverKit Extension).