Repository navigation
feat(camera): Support media-controller IR cameras (IPU6/IPU7, Qualcomm CAMSS) - #396
turbineBMW wants to merge 3 commits into
Conversation
Media-controller capture nodes (Intel IPU6/IPU7 ISYS, Qualcomm CAMSS) only stream the size the pipeline was configured for, usually by media-ctl, and their GREY rows are padded past the image width (CAMSS delivers 644-pixel rows with a 656-byte stride). - [device] keep_format = true uses the node's current format instead of negotiating one; it must still be decodable. - A GREY stride larger than the width is accepted, and each row is cut to the width before decoding. Every other stride mismatch is still rejected at open.
Some IR cameras behind a media-controller pipeline have no UVC extension unit; their illuminator is a separate LED class device, such as the Surface Pro 11's PMIC flash LED in torch mode (/sys/class/leds/ir:flash), that nothing turns on while streaming. The new device.ir_led key names that LED. Camera::open lights it at full brightness, and Camera's Drop switches it off before anything else, so it goes out on every release path, including the daemon's hold timer. The daemon writes the LED as root, so validation accepts only /sys/class/leds/<name>. Lists the key in docs/contracts.md.
Some SoC camera subsystems, such as Qualcomm CAMSS on Snapdragon X laptops, expose capture nodes only through the multi-planar API, which the v4l crate implements for neither formats nor streaming, so facelock rejected them as "not a video capture device". A new mplane module enumerates and gets or sets single-plane formats on such nodes and runs an MMAP stream with one plane per buffer; capture and device discovery take that path when a node offers only multi-planar capture. Formats with more than one plane are refused, as facelock decodes none. A failed dequeue does not re-queue a buffer the kernel still holds.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| let current = if mplane_only { | ||
| mplane::get_format(&dev) | ||
| } else { | ||
| dev.format().map(|f| mplane::Format { | ||
| width: f.width, | ||
| height: f.height, | ||
| fourcc: f.fourcc, | ||
| stride: f.stride, | ||
| }) | ||
| }; | ||
| let mut fmt = | ||
| current.map_err(|e| FacelockError::Camera(format!("failed to get format: {e}")))?; |
There was a problem hiding this comment.
💡 Edge Case: Mplane open fails on a multi-plane current format before set_format runs
current is fetched and ?-propagated on every path. On a multi-planar node, mplane::get_format goes through from_v4l2, which returns an error when num_planes != 1. Then, with keep_format = false, open fails with "failed to get format" before mplane::set_format gets a chance to request a single-plane format. That happens when the node's current format is multi-plane, for example NV12M left behind by libcamera or another app. In that branch the fetched value is never used. Only call mplane::get_format when keep_format is set.
Fetch the current format only where it is used:
let fmt = if config.keep_format {
let fmt = if mplane_only { mplane::get_format(&dev) } else {
dev.format().map(|f| mplane::Format { width: f.width, height: f.height, fourcc: f.fourcc, stride: f.stride })
}.map_err(|e| FacelockError::Camera(format!("failed to get format: {e}")))?;
// decodable check as before
fmt
} else if mplane_only {
mplane::set_format(&dev, selected_fourcc, max_w, max_h)
.map_err(|e| FacelockError::Camera(format!("failed to set format: {e}")))?
} else { /* single-planar negotiation, building mplane::Format from `set` */ };
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 closed / 1 findings🟡 Medium risk · Adding multi-planar capture and full-brightness sysfs IR control changes camera operation and could activate an unintended illuminator during capture. Adds support for media-controller IR cameras (IPU6/IPU7, Qualcomm CAMSS) with multi-planar V4L2 nodes, configurable format preservation, row padding tolerance, and LED illuminator control. Consider calling 💡 Edge Case: Mplane open fails on a multi-plane current format before set_format runs📄 crates/facelock-camera/src/capture.rs:430-441 📄 crates/facelock-camera/src/capture.rs:452-454 📄 crates/facelock-camera/src/mplane.rs:119-130
Fetch the current format only where it is used🤖 Prompt for agentsOptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
Summary
[device] keep_formatto use a media-controller node's configured format instead of negotiating one[device] ir_ledto light an LED class IR illuminator while the camera is open and switch it off first on releaseV4L2_CAP_VIDEO_CAPTURE_MPLANE(single-plane formats only), via a smallmplanemodule beside thev4lcratedocs/configuration.md,docs/contracts.mdand the config template, multi-planar nodes indocs/compatibility.md, and each change in the changelogWhy
IR cameras behind a media-controller pipeline (Intel IPU6/IPU7 ISYS, Qualcomm CAMSS on Snapdragon X laptops) only stream the size the pipeline was set up for, pad GREY rows, and on CAMSS expose multi-planar capture nodes only, so facelock either renegotiated a format the graph rejects, refused the stride at open, or reported "not a video capture device". Their illuminators can also be separate LED class devices that nothing lights during streaming. This was developed on a Surface Pro 11 (VD55G0 IR sensor on CAMSS,
ir:flashPMIC LED, 644x604 GREY in a 656-byte stride) and a Surface Pro 12 (IPU7), where enrollment,facelock testand lock-screen unlock now work.Reviewer notes
force_irquirk needs, and it has no USB identity. On my machines I corroborate a name-only quirk when the node is backed by real hardware (sysfs device outside/sys/devices/virtual) and advertises the quirk's IR-typicalformat_preference. That makes every node on the same card[IR], which conflicts with the per-node reasoning indocs/security.md, so I would rather ask how you want platform IR nodes identified before sending code.DeviceFingerprinthas no vid/pid for a platform device, so templates fall underbind_legacy_templates.ir_ledwrites only/sys/class/leds/<name>/brightness. The daemon runs as root, so validation refuses anything else, including..and nested paths.num_planes != 1. That covers GREY, Y16, YUYV and NV12 as CAMSS delivers them; true multi-plane formats stay out of scope.Validation
cargo test --workspacecargo clippy --workspace --all-targets -- -D warnings, with and without--features tpmcargo fmt --all -- --checkjust check-docs,just check-agent-docs,just check-pam-standalonefacelock testand lock-screen unlock on a Surface Pro 11 (/dev/video4, CAMSS, aarch64)