joelpurra / uvcc

USB Video Class (UVC) device configurator. Used for webcams, camcorders, etcetera. Command line interface for automation.
https://joelpurra.com/projects/uvcc/
GNU General Public License v3.0
95 stars 5 forks source link

RangeError [ERR_OUT_OF_RANGE]: The value of "byteLength" is out of range. It must be >= 1 and <= 6. Received 8 #4

Closed pjsg closed 3 years ago

pjsg commented 4 years ago

I'm running this on Max OSX 10.14.6 and the export command crashes (I have a Microsoft Lifecam Studio plugged in)

$ uvcc  --vendor 0x45e --product 0x772 export
internal/buffer.js:76
  throw new ERR_OUT_OF_RANGE(type || 'offset',
  ^

RangeError [ERR_OUT_OF_RANGE]: The value of "byteLength" is out of range. It must be >= 1 and <= 6. Received 8
    at boundsError (internal/buffer.js:76:9)
    at Buffer.readIntLE (internal/buffer.js:343:3)
    at Device.<anonymous> (/Users/philip/.nvm/versions/node/v12.16.1/lib/node_modules/uvcc/node_modules/uvc-control/index.js:175:29)
    at Transfer.<anonymous> (/Users/philip/.nvm/versions/node/v12.16.1/lib/node_modules/uvcc/node_modules/usb/usb.js:131:15) {
  code: 'ERR_OUT_OF_RANGE'
}

Any thoughts? I'm prepared to do debugging....

joelpurra commented 4 years ago

@pjsg: this is an upstream error in node-uvc-control, due to changes in Node.js. It should be fixed for the upcoming v2 release of node-uvc-control, but work seems to have stalled.

I've also backported it to node-uvc-control v1, alongside some fixes for Linux. It "works for me" but I'd appreciate if you tested them. If it works for you as well I am considering creating a release for uvcc targeting the forked code.

Further details about the different versions in this issue comment. Do they work for you?

scarrawaySF commented 4 years ago

@joelpurra I tried the forked code locally on my Mac - Seems like the export just creates an empty file now for me. I'm running node index.js --vendor 0x45e --product 0x772 export > "export.json"

pjsg commented 4 years ago

@joelpurra I also get an empty export -- but if you add --verbose, then you get

./index.js  --vendor 0x45e --product 0x772 export --verbose
Parsed arguments: {
  "_": [],
  "version": false,
  "verbose": true,
  "help": false,
  "vendor": 1118,
  "product": 1906,
  "$0": "index.js",
  "cmd": "export"
}
Error getting value, ignoring. autoExposurePriority [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
Error getting value, ignoring. brightness [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
Error getting value, ignoring. contrast [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
Error getting value, ignoring. saturation [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
Error getting value, ignoring. sharpness [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
Error getting value, ignoring. whiteBalanceTemperature [OperationalError: LIBUSB_TRANSFER_STALL] {
  cause: [Error: LIBUSB_TRANSFER_STALL] { errno: 4 },
  isOperational: true,
  errno: 4
}
RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds
    at boundsError (internal/buffer.js:74:11)
    at Buffer.readInt16LE (internal/buffer.js:404:5)
    at Buffer.readIntLE (internal/buffer.js:339:17)
    at readInt (/Users/philip/uvcc/node_modules/uvc-control/index.js:268:19)
    at readInts (/Users/philip/uvcc/node_modules/uvc-control/index.js:254:17)
    at Device.<anonymous> (/Users/philip/uvcc/node_modules/uvc-control/index.js:180:22)
    at Transfer.<anonymous> (/Users/philip/uvcc/node_modules/usb/usb.js:127:15) {
  code: 'ERR_BUFFER_OUT_OF_BOUNDS'
} uncaughtException
pjsg commented 4 years ago

I added the following line in readints in uvc-control/index.js

console.log("readints len %d, %d, %d\n", buffer.length, length, fieldSize);

and, rather surprisingly, it output:

readints len 1, 2, 2

i.e. the buffer only had 1 byte in it, in spite of being asked to read 2 bytes.

If I just make this return None, then I get

{
  "absoluteExposureTime": 156,
  "absoluteFocus": 12,
  "absolutePanTilt": [
    0,
    0
  ],
  "absoluteZoom": 0,
  "autoExposureMode": 8,
  "autoFocus": 1,
  "backlightCompensation": []
}

which looks plausible.

joelpurra commented 4 years ago

@scarrawaySF: that is the same result as reported in #3. Do you also use a Microsoft® LifeCam Studio(TM)? (Asking to verify that you didn't happen to use the wrong vendor/product ids, although I think there would also be another error trying to close after the empty output.)

@pjsg: the ERR_BUFFER_OUT_OF_BOUNDS is from inside the backported fixes (readInts). That's not a good sign. Edit: just noticed your follow-up comment with debugging values.

scarrawaySF commented 4 years ago

@joelpurra Yes I'm also using the Lifecam Studio - happy coincidence I suppose.

joelpurra commented 4 years ago

@pjsg: you'll find the per-control byte sizes in the same index.js file. In this case, check for size: 2. Might be that this particular camera doesn't send a 2-byte value for one of them. Can you figure out which one?

joelpurra commented 4 years ago

To reduce impact from other fixes in that branch, I've separated the relevant commit in my node-uvc-control fork, and targeted the fork in a uvcc branch. If there are fixes I suppose they can go there first.

Successfully tested the branches in both Linux (Ubuntu 19.10) and macOS (10.14.6 Mojave) using a Logitech c920. Tested in both Node.js v12.16.1 and v13.11.0.

Am not exactly an expert in the low-level UVC/USB fields; am mostly relying on the source code of node-uvc-control and similar. There are official size/offset values in the UVC standard though -- for example 4.2.2.1.14 PanTilt (Absolute) Control on page 89 (104 in the document) in UVC 1.5 Class specification.pdf. Would have to assume that compliant devices implement them properly, and that the issue lies in node-uvc-control.

pjsg commented 4 years ago

I changed readints to

function readInts(buffer, length, fieldSize) {
  if((length%fieldSize)!==0) throw new Error("Not equal-sized fields.");
  var output=[];
  length = Math.min(length, buffer.length);
  for (var i=0;i*fieldSize<length;i++) {
    output.push(readInt(buffer.slice(i*fieldSize), Math.min(fieldSize, length - i * fieldSize)));
  }
  if (output.length === 1) {
    return output[0];
  }
  return output;
}

and things are (sort of) working. This change to readints lines up with the current version of node-uvc-control (which does the calculations differently, but has the same effect)

After saying all of that, I have found that setting the lifecam studio brightness to 20 is too dark, and setting it to 21 is much too light. The setting only works in autoExposureMode 1. Hmm...

pjsg commented 4 years ago

In my case, it was the backlightCompensation control that has a length of 1 (and a range of 1 -> 1!)

joelpurra commented 4 years ago

@pjsg: the UVC standard seems to say 2 bytes in 4.2.2.3.1 Backlight Compensation Control on page 96 (or 111) in UVC 1.5 Class specification.pdf, which is what node-uvc-control uses. Odd that the camera would not send the same number of bytes. I would think (hope) that Microsoft built their cameras to standard, and that something else is wrong.

joelpurra commented 4 years ago

@pjsg: redefining length to Math.min(length, buffer.length) seems like a workaround for the missing second byte. Does the value for backlightCompensation seem reasonable? Would probably be best to compare to a value from another tool, such as v4l2-ctl --all if you have a Linux box around.

I think there's a chance that the second byte has a null \0 value -- which might make it "unreadable" by default, if null is a terminator character. Converting values from lower-level libraries to Node.js buffers can have that effect, as reading null values might be "out of the ordinary".

Returns a new Buffer that references the same memory as the original, but offset and cropped by the start and end indices.

const originalFourBytes = Buffer.from("abcd");
console.log(originalFourBytes.length, originalFourBytes);

const readThreeBytes = originalFourBytes.slice(0, 3);
console.log(readThreeBytes.length, readThreeBytes);

const readFourBytes = originalFourBytes.slice(0, 4);
console.log(readFourBytes.length, readFourBytes);

// NOTE: only receive four bytes, not five.
const readFiveBytes = originalFourBytes.slice(0, 5);
console.log(readFiveBytes.length, readFiveBytes);

The above hints to having to pad the bytes read from the camera, presumably with \0, to get the correct value.

This also relates to the "actual" length of the data read from the camera. Apparently there are recent libusb errors (on Windows at least) with the expected versus actual number of bytes reported, but can't say if it's only for larger actual length or also smaller. node-usb relies on this value for the slice.

joelpurra commented 4 years ago

I've updated the test branch. Am now adding extra \0 bytes at the end1 of the buffer passed to readInts, before parsing the integers.

The output doesn't change for me though, neither in Linux nor macOS. Does it help anyone else?

1 Since the bytes come from a lower level in the computer hardware (and software) architecture, the byte order and endianess might matter. If so, I am not sure how, and think it should be (have been) handled on a lower level already.

pjsg commented 4 years ago

I ran v4l2-ctl --all on a linux box with the lifecam, and this is the output:

Driver Info (not using libv4l2):
    Driver name   : uvcvideo
    Card type     : Microsoft® LifeCam Studio(TM)
    Bus info      : usb-0000:00:14.0-4
    Driver version: 1.0.0
    Capabilities  : 0x04000001
        Video Capture
        Streaming
Video input : 0 (Camera 1: ok)
Format Video Capture:
    Width/Height  : 640/480
    Pixel Format  : 'YUYV'
    Field         : None
    Bytes per Line: 1280
    Size Image    : 8320
    Colorspace    : SRGB
Crop Capability Video Capture:
    Bounds      : Left 0, Top 0, Width 640, Height 480
    Default     : Left 0, Top 0, Width 640, Height 480
    Pixel Aspect: 1/1
Streaming Parameters Video Capture:
    Capabilities     : timeperframe
    Frames per second: 5.000 (5/1)
    Read buffers     : 0
                     brightness (int)    : min=30 max=255 step=1 default=133 value=133
                       contrast (int)    : min=0 max=10 step=1 default=5 value=5
                     saturation (int)    : min=0 max=200 step=1 default=103 value=103
 white_balance_temperature_auto (bool)   : default=1 value=1
           power_line_frequency (menu)   : min=0 max=2 default=2 value=2
      white_balance_temperature (int)    : min=2500 max=10000 step=1 default=4500 value=4500
                      sharpness (int)    : min=0 max=50 step=1 default=25 value=25
         backlight_compensation (int)    : min=0 max=10 step=1 default=0 value=5
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=1 max=10000 step=1 default=156 value=156
                   pan_absolute (int)    : min=-529200 max=529200 step=3600 default=0 value=0
                  tilt_absolute (int)    : min=-432000 max=432000 step=3600 default=0 value=0
                 focus_absolute (int)    : min=0 max=40 step=1 default=0 value=0
                     focus_auto (bool)   : default=1 value=1
                  zoom_absolute (int)    : min=0 max=317 step=1 default=0 value=0
                     brightness (int)    : min=30 max=255 step=1 default=133 value=133
                       contrast (int)    : min=0 max=10 step=1 default=5 value=5
                     saturation (int)    : min=0 max=200 step=1 default=103 value=103
 white_balance_temperature_auto (bool)   : default=1 value=1
           power_line_frequency (menu)   : min=0 max=2 default=2 value=2
      white_balance_temperature (int)    : min=2500 max=10000 step=1 default=4500 value=4500
                      sharpness (int)    : min=0 max=50 step=1 default=25 value=25
         backlight_compensation (int)    : min=0 max=10 step=1 default=0 value=5
joelpurra commented 4 years ago

@pjsg: are the control's values from the above v4l2-ctl output the same as you see in uvcc? Perhaps paste the full output shown with your readInts(...) fixes, to verify that the integers are parsed correctly.

Interestingly, a whole bunch of the controls are listed twice (with the same name and values) in the v4l2-ctl output. Am unsure if this is intended, and if not, what causes it. Could it be the camera reporting some UVC properties twice? Perhaps they belong to different units, somehow?

Single

               exposure_absolute (int)    : min=1 max=10000 step=1 default=156 value=156
                   exposure_auto (menu)   : min=0 max=3 default=3 value=3
                  focus_absolute (int)    : min=0 max=40 step=1 default=0 value=0
                      focus_auto (bool)   : default=1 value=1
                    pan_absolute (int)    : min=-529200 max=529200 step=3600 default=0 value=0
                   tilt_absolute (int)    : min=-432000 max=432000 step=3600 default=0 value=0
                   zoom_absolute (int)    : min=0 max=317 step=1 default=0 value=0

Duplicated

          backlight_compensation (int)    : min=0 max=10 step=1 default=0 value=5
                      brightness (int)    : min=30 max=255 step=1 default=133 value=133
                        contrast (int)    : min=0 max=10 step=1 default=5 value=5
            power_line_frequency (menu)   : min=0 max=2 default=2 value=2
                      saturation (int)    : min=0 max=200 step=1 default=103 value=103
                       sharpness (int)    : min=0 max=50 step=1 default=25 value=25
  white_balance_temperature_auto (bool)   : default=1 value=1
       white_balance_temperature (int)    : min=2500 max=10000 step=1 default=4500 value=4500

By the way, just found this note about Microsoft LifeCam Studio (045e:0772). Might be a hardware/firmware issue?

15 Some versions of this camera have been reported to randomly time out or stall in response to valid UVC control requests, probably as a result of a race condition bug in the camera firmware. This can sometime lead to the camera failing to be recognized by the kernel. Little can be done to fix the problem.

pjsg commented 4 years ago

This is the exact data that I get from ranges and export

{
  "absoluteExposureTime": [
    1,
    10000
  ],
  "absoluteFocus": [
    0,
    40
  ],
  "absolutePanTilt": [
    [
      -529200,
      -432000
    ],
    [
      529200,
      432000
    ]
  ],
  "absoluteZoom": [
    0,
    317
  ],
  "backlightCompensation": [
    1,
    1
  ]
}

The backlight compensation is shown by v4l2-ctl as being between 1 and 10 rather than 1 to 1.

{
  "absoluteExposureTime": 156,
  "absoluteFocus": 0,
  "absolutePanTilt": [
    0,
    0
  ],
  "absoluteZoom": 0,
  "autoExposureMode": 8,
  "autoFocus": 1,
  "backlightCompensation": 1
}

The interesting differences are that v4l2-ctl shows the exposure_mode as 3 and type menu. I suspect that this is really meaning bit 3 (i.e. a value of 8). The backlight compensation shows as value 5 in vtl2-ctl whereas uvcc shows 1.

There are some extra parameters shown in the v4l2-ctl, but I expect that those could be added fairly easily.

joelpurra commented 4 years ago

Wonder if the \0 byte patch I have in the backport branch will make backlightCompensation range max 1 into 10 then? Could you confirm? I mean, it feels like it shouldn't -- and even so, the value 1 probably wouldn't turn into 5. (This reminds me that it would be good to list the step value when available.)

Yeah, the auto exposure mode is a bitmap (4.2.2.1.2 Auto-Exposure Mode Control, page 82/97). The menu type in v4l2-ctl is expanded to show options when using other flags.

The upcoming v2 version of node-uvc-control should have automatic control detection. Could of course add them to v1 as well; perhaps send a pull request to node-uvc-control?

pjsg commented 4 years ago

I thought that you were using your own version of uvc-control.....

I'm going to try and see how this camera behaves on windows. I think that I can capture the USB transactions with wireshark -- and see how they differ from the transactions on OSX.

pjsg commented 4 years ago

The windows capture shows backlight compensation being returned as two bytes with the range of 0 to 10. Curiously enough there did appear to be multiple endpoints and may behaved differently. I'm not sure if that is the root cause or not.

When I plug in the camera into OSX and I'm capturing in wireshark, then it enumerates the controls and pulls back the range of 0 - 10 for backlight compensation.

I now understand what is going on (but I don't know how to fix it).

If I change the getUnitOverride method to:

UVCControl.prototype.getUnitOverride = function(unit) {
  if (unit == UVC_INPUT_TERMINAL_ID && this.options.inputTerminalId) {
    return this.options.inputTerminalId;
  }
  if (unit == UVC_PROCESSING_UNIT_ID) {
    return 4;
  }
  if (unit == UVC_PROCESSING_UNIT_ID && this.options.processingUnitId) {
    return this.options.processingUnitId
  }
  return unit;
}

Then the output of uvcc ranges is

{
  "absoluteExposureTime": [
    1,
    10000
  ],
  "absoluteFocus": [
    0,
    40
  ],
  "absolutePanTilt": [
    [
      -529200,
      -432000
    ],
    [
      529200,
      432000
    ]
  ],
  "absoluteZoom": [
    0,
    317
  ],
  "backlightCompensation": [
    0,
    10
  ],
  "brightness": [
    30,
    255
  ],
  "contrast": [
    0,
    10
  ],
  "saturation": [
    0,
    200
  ],
  "sharpness": [
    0,
    50
  ],
  "whiteBalanceTemperature": [
    2500,
    10000
  ]
}

This is much better and seems to align with what windows sees. The CameraFactory doesn't seem to set these options and I don't know which layer ought to set those options. But I think that fixes this would help immensely.

pjsg commented 4 years ago

I think that you need to do a "get configuration descriptor" operation. When the camera is plugged in, the OS seems to do this, and it returns (partially expanded) the blob below. I expanded the bit that shows how to get the value 4 for the Processing Unit.

The OS seemed to do two requests -- the first request was for a length of 9 which returned the CONFIGURATION DESCRIPTOR below -- which includes the total length of the descriptor. The second request was for that length. I've also pasted this operation below.

Frame 610: 2381 bytes on wire (19048 bits), 2381 bytes captured (19048 bits) on interface XHC20, id 0
USB URB
    [Source: 19.7.0]
    [Destination: host]
    Darwin header bcdVersion: 0x0100
    Darwin header length: 32
    Request type: COMPLETE (1)
    I/O length [bytes]: 2349
    Request status: kIOReturnSuccess (0x00000000)
    Isochronous transfer number of frames: 0
    I/O ID: 0x000000000449d0e8
    Device location ID: 0x14130000
    Device speed: High (2)
    USB device index: 7
    Endpoint address: 0x80
    .... 0000 = Endpoint number: 0, Direction: IN
        1... .... = Direction: IN (1)
        .... 0000 = Endpoint number: 0
    Endpoint transfer type: Control (0)
    [Request in: 601]
    [Time from request: 0.031064000 seconds]
CONFIGURATION DESCRIPTOR
    bLength: 9
    bDescriptorType: 0x02 (CONFIGURATION)
    wTotalLength: 2349
    bNumInterfaces: 5
    bConfigurationValue: 1
    iConfiguration: 0
    Configuration bmAttributes: 0x80  NOT SELF-POWERED  NO REMOTE-WAKEUP
    bMaxPower: 250  (500mA)
INTERFACE ASSOCIATION DESCRIPTOR
INTERFACE DESCRIPTOR (0.0): class Video
VIDEO CONTROL INTERFACE DESCRIPTOR [Header]
    bLength: 13
    bDescriptorType: 0x24 (video class interface)
    Subtype: Header (1)
    bcdUVC: 0x0100
    wTotalLength: 87
    dwClockFrequency: 30000000
    bInCollection: 1
    baInterfaceNr: 01
VIDEO CONTROL INTERFACE DESCRIPTOR [Input Terminal] (Entity 1)
    bLength: 18
    bDescriptorType: 0x24 (video class interface)
    Subtype: Input Terminal (2)
    bTerminalID: 1
    wTerminalType: Camera Input (0x0201)
    bAssocTerminal: 0
    iTerminal: 0
    wObjectiveFocalLengthMin: 0
    wObjectiveFocalLengthMax: 0
    wOcularFocalLength: 0
    bControlSize: 3
    bmControl: 0x00020a2a, Auto Exposure Mode, Exposure Time (Absolute), Focus (Absolute), Zoom (Absolute), PanTilt (Absolute), Auto Focus
VIDEO CONTROL INTERFACE DESCRIPTOR [Output Terminal] (Entity 2)
VIDEO CONTROL INTERFACE DESCRIPTOR [Selector Unit] (Entity 3)
VIDEO CONTROL INTERFACE DESCRIPTOR [Processing Unit] (Entity 4)
    bLength: 11
    bDescriptorType: 0x24 (video class interface)
    Subtype: Processing Unit (5)
    bUnitID: 4
    bSourceID: 3
    wMaxMultiplier: 0
    bControlSize: 2
    bmControl: 0x0000155b, Brightness, Contrast, Saturation, Sharpness, White Balance Temperature, Backlight Compensation, Power Line Frequency, White Balance Temperature, Auto
    iProcessing: 0
VIDEO CONTROL INTERFACE DESCRIPTOR [Extension Unit] (Entity 5)
ENDPOINT DESCRIPTOR
VIDEO CONTROL ENDPOINT DESCRIPTOR [Interrupt]
INTERFACE DESCRIPTOR (1.0): class Video
VIDEO STREAMING INTERFACE DESCRIPTOR [Input Header]
VIDEO STREAMING INTERFACE DESCRIPTOR [Format Uncompressed]  (Format 1): YUY2
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  1):  640 x  480
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  2): 1280 x  720
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  3):  960 x  544
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  4):  800 x  448
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  5):  640 x  360
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  6):  424 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  7):  352 x  288
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  8):  320 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  9):  800 x  600
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 10):  176 x  144
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 11):  160 x  120
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 12): 1920 x 1080
VIDEO STREAMING INTERFACE DESCRIPTOR [Colorformat]
VIDEO STREAMING INTERFACE DESCRIPTOR [Format MJPEG]  (Format 2)
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  1):  640 x  480
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  2): 1920 x 1080
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  3): 1280 x  720
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  4):  960 x  544
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  5):  800 x  448
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  6):  640 x  360
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  7):  800 x  600
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  8):  432 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index  9):  352 x  288
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index 10):  176 x  144
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index 11):  320 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame MJPEG]   (Index 12):  160 x  120
VIDEO STREAMING INTERFACE DESCRIPTOR [Colorformat]
VIDEO STREAMING INTERFACE DESCRIPTOR [Format Uncompressed]  (Format 3): M420
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  1):  640 x  480
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  2): 1280 x  720
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  3):  960 x  544
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  4):  800 x  448
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  5):  640 x  360
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  6):  424 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  7):  352 x  288
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  8):  320 x  240
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index  9):  800 x  600
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 10):  176 x  144
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 11):  160 x  120
VIDEO STREAMING INTERFACE DESCRIPTOR [Frame Uncompressed]   (Index 12): 1920 x 1080
VIDEO STREAMING INTERFACE DESCRIPTOR [Colorformat]
INTERFACE DESCRIPTOR (1.1): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.2): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.3): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.4): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.5): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.6): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.7): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.8): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.9): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.10): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.11): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.12): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.13): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.14): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.15): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.16): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.17): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.18): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.19): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.20): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.21): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.22): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.23): class Video
ENDPOINT DESCRIPTOR
INTERFACE DESCRIPTOR (1.24): class Video
ENDPOINT DESCRIPTOR
INTERFACE ASSOCIATION DESCRIPTOR
INTERFACE DESCRIPTOR (2.0): class Audio
Class-specific Audio Control Interface Descriptor: Header Descriptor
Class-specific Audio Control Interface Descriptor: Input terminal descriptor
Class-specific Audio Control Interface Descriptor: Feature unit descriptor
Class-specific Audio Control Interface Descriptor: Output terminal descriptor
INTERFACE DESCRIPTOR (3.0): class Audio
INTERFACE DESCRIPTOR (3.1): class Audio
Class-specific Audio Streaming Interface Descriptor: General AS Descriptor
Class-specific Audio Streaming Interface Descriptor: Format type descriptor
ENDPOINT DESCRIPTOR
Class-specific Audio Streaming Endpoint Descriptor
INTERFACE DESCRIPTOR (4.0): class HID
HID DESCRIPTOR
ENDPOINT DESCRIPTOR

Get request

Frame 599: 40 bytes on wire (320 bits), 40 bytes captured (320 bits) on interface XHC20, id 0
USB URB
    [Source: 19.7.0]
    [Destination: host]
    Darwin header bcdVersion: 0x0100
    Darwin header length: 32
    Request type: SUBMIT (0)
    I/O length [bytes]: 9
    Request status: kIOReturnSuccess (0x00000000)
    Isochronous transfer number of frames: 0
    I/O ID: 0x000000000449d0e6
    Device location ID: 0x14130000
    Device speed: High (2)
    USB device index: 7
    Endpoint address: 0x80
    .... 0000 = Endpoint number: 0, Direction: IN
        1... .... = Direction: IN (1)
        .... 0000 = Endpoint number: 0
    Endpoint transfer type: Control (0)
    [Response in: 600]
Setup Data
    bmRequestType: 0x80
        1... .... = Direction: Device-to-host
        .00. .... = Type: Standard (0x0)
        ...0 0000 = Recipient: Device (0x00)
    bRequest: GET DESCRIPTOR (6)
    Descriptor Index: 0x00
    bDescriptorType: CONFIGURATION (0x02)
    Language Id: no language specified (0x0000)
    wLength: 9
Blisse commented 4 years ago

I am not a JS developer and the intricacies of libusb are a bit beyond me, but I want to say thank you both for the great work and also for such an involved thread.

I added from above,

  if (unit == UVC_PROCESSING_UNIT_ID) {
    return 4;
  }

and suddenly I'm able to run

/usr/local/bin/uvcc --vendor 0x45e --product 0x772 set backlightCompensation 1

and 

/usr/local/bin/uvcc --vendor 0x45e --product 0x772 set brightness 50

on my Microsoft Lifecam to adjust the crazy white balance. :)

A few of the other settings don't work as expected, but that was really the biggest one

nick-gray commented 4 years ago

I changed

  absolutePanTilt: {
    unit: UVC_INPUT_TERMINAL_ID,
    selector: 0x0D,
    size: 8 // dwPanAbsolute (4 bytes) + dwTiltAbsolute (4 bytes)
  },

to

  absolutePanTilt: {
    unit: UVC_INPUT_TERMINAL_ID,
    selector: 0x0D,
    size: 6 // dwPanAbsolute (4 bytes) + dwTiltAbsolute (4 bytes)
  },
nick-gray commented 4 years ago

Nice work @joelpurra

joelpurra commented 4 years ago

Thank you guys for testing the fixes: @pjsg, @Blisse, @nick-gray!

I thought that you were using your own version of uvc-control.....

@pjsg: yes, but I don't want to maintain a fork. All changes should be merged into the upstream node-uvc-control. The maintainer even started work on version 2, and announced it ahead of time to uvcc, but seems to have lost momentum. The Linux fixes I did for node-uvc-control last year were not verified and merged. It's not always possible to find time for open source projects, so it's understandable.

I think that you need to do a "get configuration descriptor" operation. When the camera is plugged in, the OS seems to do this, and it returns (partially expanded) the blob below. I expanded the bit that shows how to get the value 4 for the Processing Unit.

@pjsg: nice digging! Would not have thought to go as deep as using wireshark. I've cut out a bunch of lines and kept what seems relevant to this issue.

VIDEO CONTROL INTERFACE DESCRIPTOR [Input Terminal] (Entity 1)
    Subtype: Input Terminal (2)
    bTerminalID: 1
    bControlSize: 3
    bmControl: 0x00020a2a, Auto Exposure Mode, Exposure Time (Absolute), Focus (Absolute), Zoom (Absolute), PanTilt (Absolute), Auto Focus
VIDEO CONTROL INTERFACE DESCRIPTOR [Processing Unit] (Entity 4)
    Subtype: Processing Unit (5)
    bUnitID: 4
    bControlSize: 2
    bmControl: 0x0000155b, Brightness, Contrast, Saturation, Sharpness, White Balance Temperature, Backlight Compensation, Power Line Frequency, White Balance Temperature, Auto
  1. The processing unit id is 4 for your camera. This clashes with the hardcoded id of 3 in node-uvc-control v1. This may explain why your fix in getUnitOverride(...) works for you and @Blisse, since these requests would LIBUSB_TRANSFER_STALL otherwise.
  2. ~The control size for the input terminal is 3 bytes for your camera, but the hardcoded value in node-uvc-control v1 is 2. This may explain why the fix by @nick-gray works (3+3 for pan+tilt).~ Edit: the control size bControlSize is the size of the bitfield bmControls with the camera's supported controls. For an input terminal the bControlSize is always 3 (Table 3-6 Camera Terminal Descriptor, page 52 or 67). The size for pan+tilt on the input terminal should still be 8 (4+4) according to UVC 1.5 (4.2.2.1.14 PanTilt (Absolute) Control, page 89 or 104).

These things seem to have been taken care of in node-uvc-control v2, which requests these details from the camera in getInterfaceDescriptors(...).

There's plenty of intricacies in the UVC/USB standards, down to the bits and bytes level in hardware. Applying the unit id or control byte size fixes above might work for some cameras, but would instead break support for for example my camera. Dynamically detecting these per-camera values is indeed the way to go, meaning it's time to upgrade to node-uvc-control v2.

Created an experimental v2-next version of uvcc last year, which should use per-camera detection. Don't think it fixes all issues, as it did not seem to work 100% (tested by @Jip-Hop in #3), even for my camera, but could be worth a shot. Could you guys try it with your cameras, and perhaps post the results you get?

git clone "git@github.com:joelpurra/uvcc.git"
cd uvcc

git checkout feature/uvc-control-v2-next
rm -r node_modules/
npm install

./index.js --verbose ranges
./index.js --verbose export

Please include the vendor and product ids with each post. Does the output look ok? Does it require any fixes?

pjsg commented 4 years ago

My output is:

[] philip@chicken:~/uvcc$ ./index.js  --vendor 0x45e --product 0x772 ranges
{
  "backlight_compensation": {
    "min": 0,
    "max": 10
  },
  "brightness": {
    "min": 30,
    "max": 255
  },
  "contrast": {
    "min": 0,
    "max": 10
  },
  "saturation": {
    "min": 0,
    "max": 200
  },
  "sharpness": {
    "min": 0,
    "max": 50
  },
  "white_balance_temperature": {
    "min": 2500,
    "max": 10000
  }
}
[] philip@chicken:~/uvcc$ ./index.js  --vendor 0x45e --product 0x772 export
{
  "auto_exposure_mode": {
    "bAutoExposureMode": 6
  },
  "auto_white_balance_temperature": {
    "bWhiteBalanceTemperatureAuto": 1
  },
  "backlight_compensation": {
    "wBacklightCompensation": 1
  },
  "brightness": {
    "wBrightness": 133
  },
  "contrast": {
    "wContrast": 5
  },
  "power_line_frequency": {
    "bPowerLineFrequency": 2
  },
  "saturation": {
    "wSaturation": 103
  },
  "sharpness": {
    "wSharpness": 25
  },
  "white_balance_temperature": {
    "wWhiteBalanceTemperature": 4500
  }
}
[] philip@chicken:~/uvcc$ 

It is missing attributes like the zoom control..... -- but this can be fixed in uvc-control/index.js -- adjust the initialization of this.ids to be:

    this.ids = {
      processingUnit: descriptors.processingUnit.bUnitID,
      cameraInputTerminal: descriptors.cameraInputTerminal.bTerminalID,
    }

Then I get:

{
  "absolute_focus": {
    "wFocusAbsolute": 14
  },
  "absolute_pan_tilt": {
    "dwPanAbsolute": 0
  },
  "absolute_zoom": {
    "wObjectiveFocalLength": 0
  },
  "auto_exposure_mode": {
    "bAutoExposureMode": 8
  },
  "auto_focus": {
    "bFocusAuto": 1
  },
  "auto_white_balance_temperature": {
    "bWhiteBalanceTemperatureAuto": 1
  },
  "backlight_compensation": {
    "wBacklightCompensation": 1
  },
  "brightness": {
    "wBrightness": 133
  },
  "contrast": {
    "wContrast": 5
  },
  "power_line_frequency": {
    "bPowerLineFrequency": 2
  },
  "saturation": {
    "wSaturation": 103
  },
  "sharpness": {
    "wSharpness": 25
  },
  "white_balance_temperature": {
    "wWhiteBalanceTemperature": 4500
  }
}
joelpurra commented 4 years ago

@pjsg: it seems your this.ids = { ... } is unmodified compared to the file I see in the v2-control-transfer-target-device branch. Did you make any other modifications?

Judging by the missing controls, which belong to the input terminal, I suspect you made a change affecting cameraInputTerminal. A few lines down I see what looks like a broken mapping -- the property name ~is~ was expected to be inputTerminal (not cameraInputTerminal).


While not up to par with node-uvc-control v2, I've also fixed a few more basic uvcc features to get basic uvcc export | uvcc import functionality working.

git clone "git@github.com:joelpurra/uvcc.git"
cd uvcc

git checkout feature/uvc-control-v2-next
git pull
rm -r node_modules/
npm install

./index.js --verbose ranges
./index.js --verbose export

# NOTE: not all settings can be set if they're in auto-mode -- fix pending.
./index.js --verbose export | ./index.js --verbose import

There are still bugs, both in uvcc (missing functionality, setting values when they're in auto-mode, etcetera) and node-uvc-control (spotted some on the byte-reading level). Tested on macOS and Ubuntu using a Logitech C920 (0x46d/0x82d) with node v12.

Hope this also works for cameras other than the c920 =)

pjsg commented 4 years ago

My change was to change an inputTerminal into a cameraInputTerminal.

joelpurra commented 4 years ago

See https://github.com/makenai/node-uvc-control/pull/64.

joelpurra commented 3 years ago

Released uvcc v2.0.0 -- hopefully this has been fixed! Please open a new issue if there are any problems with the new release.