Skip to content

fix(camera): collapse a Brio's IR node into one Linux camera entry - #1234

Open
4ni1ak wants to merge 2 commits into
AprilNEA:masterfrom
4ni1ak:fix/brio-webcam-duplicate
Open

fix(camera): collapse a Brio's IR node into one Linux camera entry#1234
4ni1ak wants to merge 2 commits into
AprilNEA:masterfrom
4ni1ak:fix/brio-webcam-duplicate

Conversation

@4ni1ak

@4ni1ak 4ni1ak commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the Brio webcam showing up twice in the device list on Linux.
  • The Brio exposes two capture-capable /dev/videoN nodes: the main color sensor and a low-resolution node feeding its IR sensor (used for Windows Hello-style auth). Both nodes pass VIDIOC_ENUM_FMT's capture-format check in linux.rs::is_capture_node(), so both were previously classified as camera nodes and listed separately — matching the reporter's openlogi list output (4096x2160@120 and 340x340@30, same vendor/product id and serial).
  • Root cause: node classification (capture vs. metadata node) is correct, but nothing collapsed sibling capture nodes belonging to the same physical USB device back into one listing entry.

Changes

  • crates/openlogi-camera/src/linux.rs:
    • Node now also carries the canonicalized sysfs directory of the underlying USB device (not the per-node USB interface VIDIOC_QUERYCAP exposes), shared by every capture node one physical camera exposes.
    • Added cameras() / merge_by_usb_device(), which group described nodes by that shared USB device directory and keep only the highest-resolution entry per group.
    • node_for_unique_id() (used by the control and capture-stream paths) is untouched and still resolves every node individually, so a secondary node (e.g. the IR node) stays independently addressable if some other code path ever needs it.
    • Added unit tests covering the Brio dedup case and that distinct physical devices stay separate.
  • crates/openlogi-camera/src/lib.rs: Linux's enumerate_all() now calls linux::cameras() instead of mapping linux::nodes() directly.

This is Linux-only, behind #[cfg(target_os = "linux")] — macOS (AVFoundation) and Windows (DirectShow/Media Foundation) enumeration paths are untouched.

Testing

  • cargo test -p openlogi-camera — includes new tests brio_ir_node_collapses_into_the_main_capture_node, distinct_usb_devices_stay_separate, resolution_area_treats_unknown_resolution_as_smallest.
  • cargo fmt --all -- --check
  • Affected-package tier (cargo tree --workspace --target all --invert openlogi-cameraopenlogi-camera, openlogi-cli, openlogi, openlogi-desktop, openlogi-permissions):
    • cargo clippy -p openlogi-camera -p openlogi-cli -p openlogi -p openlogi-permissions --all-targets -- -D warnings
    • cargo clippy -p openlogi-desktop --all-targets -- -D warnings
    • cargo test -p openlogi-camera -p openlogi-cli -p openlogi -p openlogi-permissions -p openlogi-desktop
  • Not runtime-tested on real Brio hardware — this machine has no Brio attached. The dedup logic is unit-tested against the exact node shapes from the reporter's openlogi list output (same vendor/product id and serial, differing max resolution).
  • No Windows or macOS toolchain available here, but the change is scoped entirely to linux.rs behind cfg(target_os = "linux"), so it cannot affect those platforms' enumeration paths.

Fixes #1191

@4ni1ak
4ni1ak requested a review from AprilNEA as a code owner September 1, 2026 22:15
@greptile-apps

greptile-apps Bot commented Sep 1, 2026

Copy link
Copy Markdown

Greptile Summary

The PR groups Linux V4L2 nodes by their canonical USB-device sysfs directory and retains one camera entry per device. The revised unknown-resolution fallback still depends on incidental device-node ordering, so it can retain an IR endpoint instead of the primary color endpoint.

  • Adds USB-device identity to Linux camera nodes.
  • Deduplicates sibling capture nodes and prefers the larger known resolution.
  • Routes Linux enumeration through the grouped camera list.
  • Adds unit coverage for Brio-style sibling nodes and unknown resolutions.

Confidence Score: 4/5

This PR should not merge until deduplication identifies the primary color endpoint without relying on incidental device-node ordering.

The attempted fix for unknown primary resolution still preserves whichever sibling sorts first, allowing a secondary or IR endpoint to become the capture and control identifier.

Files Needing Attention: crates/openlogi-camera/src/linux.rs

Important Files Changed

Filename Overview
crates/openlogi-camera/src/linux.rs Adds USB-device grouping and resolution-based winner selection, but unknown resolution leaves endpoint selection dependent on lexicographic /dev/videoN ordering.
crates/openlogi-camera/src/lib.rs Switches Linux enumeration to the new grouped camera list without otherwise changing platform behavior.

Fix all with Greploop Fix All in Codex Fix All in Claude Code

Reviews (4): Last reviewed commit: "fix(camera): treat an unknown max_resolu..." | Re-trigger Greptile

Comment thread crates/openlogi-camera/src/linux.rs Outdated
@4ni1ak
4ni1ak force-pushed the fix/brio-webcam-duplicate branch from 832f3dc to dc9bf93 Compare September 1, 2026 22:23
@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly platform: linux Linux-specific issue labels Sep 3, 2026
On Linux, the Brio exposes two capture-capable /dev/videoN nodes over
UVC: the main color sensor and a low-resolution node feeding its IR
sensor for Windows Hello. VIDIOC_ENUM_FMT reports capture formats on
both, so linux.rs::is_capture_node() classified both as camera nodes
and enumerate_cameras() listed the same physical webcam twice, with
only one of the two entries carrying real controls.

Group V4L2 nodes by their shared USB device sysfs directory (not the
per-interface sysfs entry VIDIOC_QUERYCAP exposes) and keep only the
highest-resolution node per group, so one physical camera again yields
one Camera. node_for_unique_id() still resolves every node
individually, so a secondary node stays reachable if some other code
path needs it.

Fixes AprilNEA#1191
merge_by_usb_device picked the highest-resolution node in each USB device
group to decide which /dev/videoN capture node represents the camera.
resolution_area(None) returned 0, so a primary sensor whose frame-size
enumeration only reports stepwise/continuous ranges (or fails outright)
lost to any sibling node with a discrete size — including a Brio's tiny
IR sensor — and the returned unique_id pointed preview/control operations
at the wrong node.

Resolution now only decides the winner when both nodes report a known
size; an unknown resolution no longer outranks, nor is outranked by, a
known one, and the first-seen node keeps its place instead.
@4ni1ak
4ni1ak force-pushed the fix/brio-webcam-duplicate branch from f012613 to e64b04f Compare September 7, 2026 14:37
Comment on lines +141 to +146
if let (Some(candidate), Some(current)) =
(camera.max_resolution, best.max_resolution)
&& resolution_area(candidate) > resolution_area(current)
{
*best = camera;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unknown resolution selects wrong endpoint

When the primary color node has an unknown resolution and an IR node sorts first—for example, /dev/video10 before /dev/video2merge_by_usb_device() preserves the IR node. Its unique_id then becomes the capture and control identifier, so preview and control operations open the secondary sensor while the primary color endpoint disappears from enumeration.

Knowledge Base Used: Camera control integration

Fix in Codex Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: linux Linux-specific issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Brio Webcam is listed twice

2 participants