[Feature]: Field of view and Show Mode live in vendor extension units, and nothing in OpenLogi can address one
- Dominant language
- Rust
- Stars
- 21k
- Forks
- 675
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 172
Description
### Pre-flight checklist
- [x] I searched existing issues and the [Roadmap](https://github.com/AprilNEA/OpenLogi#roadmap), and this isn't already tracked.
### Problem / motivation
Every control in the shared vocabulary is a standard UVC selector on the Camera Terminal or the Processing Unit. That covers most of what a webcam exposes, and #663 and #1020 are widening it further. But not everything on Logitech cameras lives there. It lives in the vendor extension units the camera declares right alongside them, and nothing in OpenLogi can address one.
On a Brio 500 two of those are settings people reach for constantly in Logi Tune:
- **Field of view**, three steps (90, 78 and 65 degrees). This is the setting that decides whether a call sees your whole room or just you.
- **Show Mode**, the desk view that reorients the frame when the lens is tilted down at the table.
This is the same shape as #663, one level further out: the gap is not in a backend, it is that `crates/openlogi-camera` has no notion of an extension unit at all.
**This does not need a model table, and that is the part worth checking first.** The crate documents its own premise as detection keyed off the vendor id, "with no model table to maintain", and I would not want to be the change that breaks it. It does not have to be: the camera declares its extension units in its own configuration descriptor, and the walk in `uvc.rs` already reads that exact blob to find the Processing Unit. Each extension unit descriptor carries a 16 byte GUID and a `bmControls` bitmap of the selectors it implements. So "does this camera have Logitech's video pipe unit, and does that unit implement selector 5" is a question the device answers about itself.
Straight from the descriptor on this camera:
```
=== EXTENSION UNIT ===
unitID : 10
GUID : {49E40215-F434-47FE-B158-0E885023E51B}
numControls : 13
bControlSize : 2
bmControls : BA 1F
-> selectors : 2 4 5 6 8 9 10 11 12 13
```
The unit id is not a constant to hardcode. The GUID is the stable name, the descriptor supplies the id, and a camera that declares no such GUID gains nothing and shows nothing new. One quirk to carry into code: `numControls` says 13 while the bitmap sets 10 bits, and probing found exactly those 10 selectors, so the bitmap is the authority and the count is decoration.
The two selectors, probed read only over the control endpoint:
```
Unit 10 {49E40215-F434-47FE-B158-0E885023E51B}
sel 5 len=1 info=0x03 GET SET <- field of view
CUR: 02 MIN: 00 MAX: FF RES: 01 DEF: 00
sel 13 len=1 info=0x03 GET SET <- Show Mode
CUR: 01 MIN: 00 MAX: 01 RES: 01 DEF: 00
```
One trap is visible in that dump and worth stating before anyone writes code against it: **field of view reports MIN 0 and MAX 255, but only 0, 1 and 2 do anything** (90, 78 and 65 degrees). A write of 3 or 255 even succeeds on the wire and is then discarded: the value reads back unchanged and the picture does not change. Its own reported range cannot be used to build the control. `ControlRange` already carries `value_mask` for exactly this case, and today only the V4L2 backend ever populates it.
### Proposed solution
Three parts, in order of how much they touch:
1. **Collect extension units during the descriptor walk.** `scan_descriptors` returns as soon as it sees the Processing Unit. On this camera every extension unit descriptor sits after that point, so the walk has to continue to the end of the VideoControl block and collect a GUID to unit id map (with each unit's `bmControls`).
2. **A third entity in the spec table.** `Unit` is `CameraTerminal | Processing` today. Extension units add a variant carrying the GUID, and `entity()` resolves it through the map from step 1, reporting `Unsupported` when the camera does not declare that GUID. This lands in the per control spec table from #622, which is where a new addressing mode belongs.
3. **The two controls themselves.** `FieldOfView` as a discrete three value control (through `value_mask`, so an unexpected firmware reports fewer choices rather than a broken slider), and `ShowMode` as a boolean, which is how the low light toggle already works.
The GUI needs no new idea for either. Field of view is the anti-flicker chip row with different labels, so the honest move is probably to generalize that row into a reusable choice row rather than add a third copy of it.
**Platform coverage, stated plainly, because this is the part I would want vetoed early if it is going to be.** I can implement and verify macOS. The other two backends cannot reach extension units through the mechanisms they use today: Linux would need the `uvcvideo` extension unit ioctls, Windows an `IKsControl` node rather than `IAMVideoProcAmp`. I have neither machine to test against, so I would map both to `Unsupported` with a comment saying why, which is what `CameraControl::Tint` already does on Linux. If a control that only one platform can serve is not acceptable here, I would much rather hear it now than after the diff exists.
### Alternatives considered
**Logi Tune.** It is what people use today, and it is the software this project exists to replace. Its field of view setting persists in the camera's own flash, so you can set it and quit. That cuts both ways: the setting survives, but there is no way to change it without installing the vendor application.
**Hardcoding unit ids per model.** Rejected, and this is the whole design question. The GUID is right there in the descriptor, and reading it is the difference between a capability probe and a model table.
**Doing the full Logi Tune feature set in one go.** Not proposed, deliberately. Some of it should not be in OpenLogi at all, see the note below.
### Related area(s)
- [x] GUI
- [x] CLI
- [ ] Button actions / remapping
- [ ] DPI
- [ ] SmartShift
- [ ] Per-application profiles
- [ ] Configuration (TOML)
- [ ] Auto-update
- [x] Other
### Additional context
**Ordering against #1020.** The two controls here are single byte values, so they need nothing that PR adds to the payload machinery. The collision is more mundane: both changes add rows to the same spec table in `uvc.rs`, and the HDR note below only comes true once its backlight compensation lands. I would rather rebase onto it than race it. Is that the one to wait for, or is there something else in flight in `uvc.rs` I should sequence behind?
**HDR is not part of this, and that is not an oversight.** On this camera the HDR switch in Logi Tune is not an extension unit at all: it writes `PU_BACKLIGHT_COMPENSATION`, which #1020 is already adding. Once that merges, HDR on a Brio 500 works with no further code.
**What I am deliberately not proposing.** RightSight, the automatic framing, is tempting because it is the headline feature, and its on and off switch does live in an extension unit. It should not ship here, because the switch is the only part that lives in the camera: the framing itself is Logitech's LogiRightSight daemon on the host, which pulls preview frames, runs the detection, and steers the crop through the standard pan, tilt and zoom controls. Shipping it for real would mean rebuilding that pipeline inside OpenLogi, which is a computer vision application in its own right, not a camera control. Writing the switch only has an effect while that daemon is running. The blob it lives in is also less a device setting than a mailbox between Logitech's own applications: alongside the flag it carries the id of the application that wrote it and a priority ladder whose tiers are named after those applications, changes to it fire a notification the vendor software listens for, and Logi Tune corrects values it considers illegal. OpenLogi could get a write accepted by impersonating Logi Tune, but a value written under another vendor's name, into a store that vendor polices, is not a setting OpenLogi owns: whether it sticks is decided by rules only Logitech knows and is free to change. A toggle that writes cleanly, reads back cleanly, and does nothing at all unless the vendor software is installed is worse than no toggle. The built in microphone switch is out for a related reason: it writes fine, but does not take effect until the camera re-enumerates, and software cannot trigger that while the OS video driver owns the device.
**What has actually been verified, and how.** Descriptor and control probes and a handful of writes, all through small standalone IOKit tools rather than OpenLogi, with frames captured by the `imagesnap` command line tool. Writing 0 and then 2 to unit 10 selector 5 visibly widens and then narrows the frame: at 90 degrees the capture takes in both neighbouring desks and the doorway, at 65 degrees it crops to the person. Writes of 3 and 255 were tried as well; that is where the discard behaviour above was measured. The value was restored afterwards and the restored frame matches the baseline. Show Mode reads and writes as an ordinary 0/1 selector. Control requests ride the default control endpoint, so none of this needs the video interface and it works while another application is streaming.
**Environment.** macOS 15.7.3 (Sequoia), Apple Silicon (aarch64). Logitech Brio 500 (`046d:0943`), firmware 0.21, wired USB. Logi Tune not running during the measurements.
**Form notes.** The Related area(s) list has no camera option, so GUI, CLI and Other are checked. The pre-flight checklist links a Roadmap anchor that no longer exists in the README; I searched issues and pull requests instead, for "camera", "webcam", "brio", "extension unit", "RightSight", "HDR", "field of view" and "UVC". Nothing open or closed covers vendor extension units. #89 asked for Brio support in general and was closed as completed when the camera stack landed, which answered the standard UVC half of that question and not this one.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with scan_descriptors in crates/openlogi-camera and the existing uvc.rs Processing Unit walk, then inspect the per-control spec table and ControlRange.value_mask. The work is done when declared extension units can be resolved by GUID, FieldOfView and ShowMode work where supported, unsupported platforms remain explicit, and the GUI and CLI expose the controls.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, rust
- Domain
- desktop
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100