2026-06-11 23:05:17 -04:00
|
|
|
|
# liblcameraDev: libcamera device provider library
|
|
|
|
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
|
|
`liblcameraDev.so` (`commonLibs/lcameraDev/`) is a Salmanoff common library that
|
|
|
|
|
|
wraps [libcamera](https://libcamera.org/) and exposes a small, dlopen-able C
|
|
|
|
|
|
API for acquiring refcounted camera device handles. It is the camera analogue of
|
|
|
|
|
|
`libxcbXorg.so` and `liblivoxProto1.so`: it owns transport and device lifecycle,
|
|
|
|
|
|
not stim-buffer semantics.
|
|
|
|
|
|
|
|
|
|
|
|
The eventual `lcameraBuff` stim-buff-api (`stimBuffApis/lcameraBuff/`) loads
|
|
|
|
|
|
this library dynamically (via SMO search paths), resolves cameras from DAP
|
|
|
|
|
|
`deviceSelector` strings, and shares one capture session per physical camera
|
|
|
|
|
|
across multiple DAP attachments (e.g. separate H/S/V channel lines).
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
+edev|cam0|colour-hsv-h()|lcameraBuff()|lcameraDev()|lcamera-id:...||
|
|
|
|
|
|
+edev|cam0|colour-hsv-s()|lcameraBuff()|lcameraDev()|lcamera-id:...||
|
|
|
|
|
|
+edev|cam0|postrin(...)|negtrin(...)|colour-hsv-v()|lcameraBuff()|lcameraDev()|lcamera-id:...||
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The **`dev-identifier`** (`cam0` above) is the same on all three lines: one
|
|
|
|
|
|
logical camera device, three stim-feature attachments (H, S, V). The
|
|
|
|
|
|
**`deviceSelector`** (provider column) is also typically identical across those
|
|
|
|
|
|
lines so they resolve to the same physical camera and share one capture session.
|
|
|
|
|
|
|
|
|
|
|
|
Layering:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
lcameraBuff (stimBuffApi) SMO-facing producers, channel fan-out, intrins
|
|
|
|
|
|
└─ dlopen ─> lcameraDev (commonLib) libcamera manager, device refcounting
|
|
|
|
|
|
└─ libcamera0.2 + libcamera-ipa (system packages)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Channel splitting, colourspace conversion, threshold masks, and stencils belong
|
|
|
|
|
|
in a separate shared raster library (`rasterStimulus`, future) and in
|
2026-06-13 12:02:04 -04:00
|
|
|
|
`lcameraBuff`; `lcameraDev` stops at selector resolution, `CameraManager`
|
|
|
|
|
|
lifecycle, and a refcounted, **acquired** `libcamera::Camera` handle per
|
2026-06-13 20:56:33 -04:00
|
|
|
|
resolved device. Session mode negotiation (width, height, colour-space,
|
|
|
|
|
|
fully-planar YUV requirement) happens in `lcameraDev` before capture starts.
|
|
|
|
|
|
Frame buffers, request queues, and capture timing belong in `lcameraBuff` (and
|
|
|
|
|
|
supporting libraries), not here.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
## Why libcamera (for now)
|
|
|
|
|
|
|
|
|
|
|
|
Initial development targets libcamera rather than raw V4L2 so that complex Linux
|
|
|
|
|
|
camera pipelines (Media Controller graphs, platform ISPs, Raspberry Pi CSI
|
|
|
|
|
|
modules) are handled by libcamera’s pipeline handlers. Plain USB UVC webcams are
|
|
|
|
|
|
also supported via libcamera’s `uvcvideo` pipeline on many distributions.
|
|
|
|
|
|
|
|
|
|
|
|
This is a **Linux-only** path. It does not provide cross-Unix portability by
|
|
|
|
|
|
itself; a future `v4l2Linux` provider remains the fallback for generic
|
|
|
|
|
|
`/dev/videoN` access without libcamera.
|
|
|
|
|
|
|
|
|
|
|
|
Runtime packages (Debian/Ubuntu naming):
|
|
|
|
|
|
|
|
|
|
|
|
| Package | Purpose |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `libcamera0.2` | Core `libcamera.so`, pipeline handlers, `CameraManager` |
|
|
|
|
|
|
| `libcamera-ipa` | IPA plugin modules (required on RPi, Rockchip, IPU3, etc.) |
|
|
|
|
|
|
| `libcamera-dev` | Headers and pkg-config (build only) |
|
|
|
|
|
|
|
|
|
|
|
|
`libcamera-v4l2` (`v4l2-compat.so`) is **not** required for native libcamera
|
|
|
|
|
|
clients; it exists only to wrap legacy V4L2 applications via `LD_PRELOAD`.
|
|
|
|
|
|
|
|
|
|
|
|
## DAP roles
|
|
|
|
|
|
|
|
|
|
|
|
General line shape (see also `docs/deviceAttachmentPipelineSpec.md`):
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
sensor-type|dev-identifier|postrin-spec|negtrin-spec|quale-iface-api|stim-buff-api|provider|deviceSelector
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
For camera colour channels:
|
|
|
|
|
|
|
|
|
|
|
|
| DAP segment | Value | Role |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `dev-identifier` | e.g. `cam0` | User-defined name for this **device instance**; same across H/S/V lines |
|
|
|
|
|
|
| `quale-iface-api` | `colour-hsv-h()`, `colour-hsv-s()`, `colour-hsv-v()` | Names the stim feature / greyscale channel plane |
|
|
|
|
|
|
| `stim-buff-api` | `lcameraBuff()` | Salmanoff producer plugin; maps qualeIface → channel |
|
|
|
|
|
|
| `provider` | `lcameraDev()` | Selects the libcamera-backed device access path |
|
|
|
|
|
|
| `deviceSelector` | see below | Identifies which physical camera to open (often repeated on each line) |
|
|
|
|
|
|
|
|
|
|
|
|
Intrinsic thresholds on the brightness/value channel live in `postrin(...)` /
|
|
|
|
|
|
`negtrin(...)` segments on the `colour-hsv-v()` line only, following
|
|
|
|
|
|
`docs/design/intrin-thresholds.md`.
|
|
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
+edev|cam0|colour-hsv-h()|lcameraBuff()|lcameraDev()|lcamera-id:/base/soc/i2c@.../imx219@10||
|
|
|
|
|
|
+edev|cam0|colour-hsv-s()|lcameraBuff()|lcameraDev()|lcamera-id:/base/soc/i2c@.../imx219@10||
|
|
|
|
|
|
+edev|cam0|negtrin(interest-thr=40|distraction-thr=70|intolerable-thr=90)|postrin(interest-thr=30|distraction-thr=50|stupefaction-thr=80)|colour-hsv-v()|lcameraBuff()|lcameraDev()|lcamera-id:/base/soc/i2c@.../imx219@10||
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Compound selector (multiple criteria on one line):
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
+edev|cam0|colour-hsv-h()|lcameraBuff()|lcameraDev()|lcamera-id:foo bar baz;model:aaaa||
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
All three lines share one underlying `lcameraDev` capture session keyed by the
|
|
|
|
|
|
resolved libcamera camera ID. `lcameraBuff` must not open the same camera twice.
|
|
|
|
|
|
|
|
|
|
|
|
### `lcameraDev()` provider params
|
|
|
|
|
|
|
|
|
|
|
|
Reserved for future options. Empty parentheses are valid:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
lcameraDev()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Possible future params (not implemented in v1):
|
|
|
|
|
|
|
|
|
|
|
|
* `ipa-dir=...` — override IPA module search path
|
|
|
|
|
|
* `log-level=...` — map to `LIBCAMERA_LOG_LEVELS` for this provider instance
|
|
|
|
|
|
|
|
|
|
|
|
### `lcameraBuff()` stim-buff-api params
|
|
|
|
|
|
|
|
|
|
|
|
Reserved for per-producer options (frame rate caps, pixel format preference).
|
|
|
|
|
|
Channel selection is driven by the qualeIface name, not by stim-buff-api params.
|
|
|
|
|
|
|
|
|
|
|
|
Possible future params:
|
|
|
|
|
|
|
|
|
|
|
|
* `fps-hz=30`
|
|
|
|
|
|
* `width=640|height=480`
|
2026-06-13 12:02:04 -04:00
|
|
|
|
* `pixfmt=NV12` — negotiation hint for `lcameraBuff` stream setup (not
|
|
|
|
|
|
`lcameraDev`)
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
## Device selector format
|
|
|
|
|
|
|
|
|
|
|
|
libcamera’s primary stable identifier is `Camera::id()`: an opaque string,
|
|
|
|
|
|
unique per camera on the system, stable across reboot when physical topology is
|
|
|
|
|
|
unchanged. libcamera properties such as `Model` are descriptive but **not**
|
|
|
|
|
|
guaranteed unique; there is no standard `SerialNumber` property in the core
|
|
|
|
|
|
property set.
|
|
|
|
|
|
|
|
|
|
|
|
### Single criterion
|
|
|
|
|
|
|
|
|
|
|
|
`lcameraDev` resolves each criterion using a typed `prefix:value` form. If the
|
|
|
|
|
|
selector contains no `;` and no `prefix:` delimiter, the entire string is
|
|
|
|
|
|
treated as a literal `lcamera-id:` value (opaque libcamera ID).
|
|
|
|
|
|
|
|
|
|
|
|
| Prefix | Example | Match rule |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `lcamera-id:` | `lcamera-id:/base/soc/i2c@1/imx219@10` | Exact `Camera::id()` |
|
|
|
|
|
|
| *(bare string)* | `/base/soc/i2c@1/imx219@10` | Same as `lcamera-id:` |
|
|
|
|
|
|
| `index:` | `index:0` | Nth camera from `CameraManager::cameras()` |
|
|
|
|
|
|
| `model:` | `model:imx219` | Exact match on `properties::Model` |
|
|
|
|
|
|
| `model-substr:` | `model-substr:Logitech` | Substring match on `properties::Model` |
|
|
|
|
|
|
| `location:` | `location:external` | `front`, `back`, or `external` |
|
|
|
|
|
|
|
|
|
|
|
|
The value after `:` runs to the next `;` or end of string. Values may contain
|
|
|
|
|
|
spaces (e.g. `lcamera-id:foo bar baz`). Leading/trailing whitespace on each
|
|
|
|
|
|
value is stripped after parsing.
|
|
|
|
|
|
|
|
|
|
|
|
### Compound selectors (semicolon-separated)
|
|
|
|
|
|
|
|
|
|
|
|
A `deviceSelector` may contain **multiple criteria** separated by `;`. Every
|
|
|
|
|
|
criterion must match the **same** enumerated camera (**AND** semantics). This
|
|
|
|
|
|
lets operators combine a stable libcamera ID with a sanity-check on model name,
|
|
|
|
|
|
or narrow a fuzzy selector with an extra constraint.
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
lcamera-id:foo bar baz;model:aaaa
|
|
|
|
|
|
location:external;model-substr:Logitech
|
|
|
|
|
|
index:0;model:imx219
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Parsing rules:
|
|
|
|
|
|
|
|
|
|
|
|
1. Split the selector on unescaped `;` into one or more clauses (a single clause
|
|
|
|
|
|
with no `;` is a compound selector of length 1).
|
|
|
|
|
|
2. Parse each clause as `prefix:value` (or bare opaque ID if no `:` is present
|
|
|
|
|
|
in that clause only).
|
|
|
|
|
|
3. A camera is a candidate only if it satisfies **all** clauses.
|
|
|
|
|
|
4. **Zero candidates** → attach failure with diagnostics.
|
|
|
|
|
|
5. **More than one candidate** → attach failure (ambiguous); never pick
|
|
|
|
|
|
arbitrarily.
|
|
|
|
|
|
6. Exactly one candidate → resolve session by that camera’s `Camera::id()`.
|
|
|
|
|
|
|
|
|
|
|
|
If a value must contain a literal `;`, escape it as `\;` in the selector string
|
|
|
|
|
|
(same backslash-discard convention as whitespace escaping elsewhere in DAP
|
|
|
|
|
|
tokens).
|
|
|
|
|
|
|
|
|
|
|
|
Resolution algorithm (summary):
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
1. Start `CameraManager` in `lcameraDev_main` (once per process).
|
2026-06-11 23:05:17 -04:00
|
|
|
|
2. Enumerate cameras; build an identity record per camera (`id`, `model`,
|
|
|
|
|
|
`location`, `systemDevices`).
|
|
|
|
|
|
3. Parse `deviceSelector` into criterion clauses; apply **all** clauses (AND).
|
|
|
|
|
|
4. **Zero matches** → attach failure with diagnostic listing known cameras.
|
|
|
|
|
|
5. **Multiple matches** → attach failure (ambiguous selector); never pick
|
|
|
|
|
|
arbitrarily.
|
|
|
|
|
|
6. Return or create a refcounted `LcameraDevice` for the winning `Camera::id()`.
|
|
|
|
|
|
|
|
|
|
|
|
Discovery helper: `lcameraDev` should expose a list/print entry point (used by
|
|
|
|
|
|
`lcameraBuff` verbose mode or a small CLI in `libcamera-tools` style) that prints
|
|
|
|
|
|
each camera’s `id()`, `model`, and `location` so operators can copy a stable
|
|
|
|
|
|
`lcamera-id:` into DAP specs after first install.
|
|
|
|
|
|
|
|
|
|
|
|
## Shared session and refcounting
|
|
|
|
|
|
|
|
|
|
|
|
Unlike X11 windows, a camera has no “sub-objects” inside its feed — the selector
|
2026-06-13 12:02:04 -04:00
|
|
|
|
always designates the whole physical camera. Multiple DAP lines for H, S, and V
|
|
|
|
|
|
share one **device session** (acquired `libcamera::Camera`), all under the same
|
|
|
|
|
|
`dev-identifier` (e.g. `cam0`).
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
dev-identifier cam0 + deviceSelector (resolved) ──> LcameraDeviceSession (refcounted)
|
|
|
|
|
|
│
|
|
|
|
|
|
┌─────────────┼─────────────┐
|
|
|
|
|
|
▼ ▼ ▼
|
|
|
|
|
|
lcameraBuff lcameraBuff lcameraBuff
|
|
|
|
|
|
(colour-hsv-h) (colour-hsv-s) (colour-hsv-v + intrins)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Rules:
|
|
|
|
|
|
|
|
|
|
|
|
* First `getOrCreate` for a resolved camera ID: enumerate, `acquire()` the
|
2026-06-13 12:02:04 -04:00
|
|
|
|
`libcamera::Camera`, create the session entry.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
* Subsequent `getOrCreate` for the same ID: increment refcount; return the same
|
|
|
|
|
|
session handle.
|
2026-06-13 12:02:04 -04:00
|
|
|
|
* Last `release`: `release()` the libcamera camera and erase the session entry.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
* Different `deviceSelector` strings that resolve to the same libcamera ID share
|
|
|
|
|
|
one session (even if the selector text differs, e.g. one line uses
|
|
|
|
|
|
`lcamera-id:...` and another uses a compound selector that resolves to the
|
|
|
|
|
|
same camera).
|
|
|
|
|
|
* Attachments with the same `dev-identifier` and equivalent resolved camera are
|
|
|
|
|
|
the same physical device from SMO’s perspective; channel differences come from
|
|
|
|
|
|
the qualeIface name on each DAP line.
|
|
|
|
|
|
|
2026-06-13 20:56:33 -04:00
|
|
|
|
## Session mode configuration (Stage 1)
|
|
|
|
|
|
|
|
|
|
|
|
Before `lcameraBuff` starts capture, each producer calls
|
|
|
|
|
|
`lcameraDev_configureSessionModeCReq` on the shared `CameraSession` with the
|
|
|
|
|
|
requested mode:
|
|
|
|
|
|
|
|
|
|
|
|
| Field | Role |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `width` / `height` | Requested capture dimensions (non-zero) |
|
|
|
|
|
|
| `colourSpace` | Semantic colour model (`Yuv` in v1) |
|
|
|
|
|
|
| `fullPlanarIsOptional` | Default `false` — must select fully planar YUV |
|
|
|
|
|
|
|
|
|
|
|
|
`lcameraDev` chooses a concrete libcamera pixel format (e.g. YUV420) from the
|
|
|
|
|
|
camera’s supported formats. DAPS lines name the semantic colour-space, not a
|
|
|
|
|
|
raw fourcc.
|
|
|
|
|
|
|
|
|
|
|
|
Rules:
|
|
|
|
|
|
|
|
|
|
|
|
* Configure only while the session exists and capture has not started.
|
|
|
|
|
|
* **Identical** configure request on an already-configured session is a **no-op**
|
|
|
|
|
|
(multiple `lcameraBuff` producers sharing the same device selector each call
|
|
|
|
|
|
get-or-create then configure with the same mode).
|
|
|
|
|
|
* **Different** configure request on an already-configured session throws
|
|
|
|
|
|
(conflicting mode requests on the same physical camera).
|
|
|
|
|
|
* `fullPlanarIsOptional == false` (default): must select a fully planar YUV
|
|
|
|
|
|
format or throw with candidate-format diagnostics.
|
|
|
|
|
|
* `fullPlanarIsOptional == true`: rejected at the configure API until
|
|
|
|
|
|
`lcameraBuff` implements non-planar producer deinterleaving (Stage 2). The
|
|
|
|
|
|
policy helper still accepts optional planar selection for future use.
|
|
|
|
|
|
|
|
|
|
|
|
Streaming, frame delivery, and per-frame colourspace work remain **out of scope**
|
|
|
|
|
|
for `lcameraDev`; `lcameraBuff` uses the configured session to allocate buffers
|
|
|
|
|
|
and start capture on attach.
|
2026-06-13 12:02:04 -04:00
|
|
|
|
|
|
|
|
|
|
## dlopen API
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
Exported from `liblcameraDev.so` using `extern "C"` symbols (mirroring
|
|
|
|
|
|
`livoxProto1`). `lcameraBuff` loads the library with `dlopen` + `dlsym` and
|
2026-06-13 12:02:04 -04:00
|
|
|
|
calls through function pointers. Hot-path operations are **`*CReq` coroutine
|
|
|
|
|
|
invokers** (`sscl::co::ViralNonPostingInvoker`); `main` / `exit` remain
|
|
|
|
|
|
synchronous.
|
|
|
|
|
|
|
|
|
|
|
|
Header: `commonLibs/lcameraDev/lcameraDev.h`.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
### Lifecycle
|
|
|
|
|
|
|
|
|
|
|
|
```c
|
2026-06-13 12:02:04 -04:00
|
|
|
|
typedef void lcameraDev_mainFn(
|
|
|
|
|
|
const std::shared_ptr<sscl::ComponentThread>& componentThread);
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
typedef void lcameraDev_exitFn(void);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
`lcameraDev_main` records the Body `ComponentThread`, starts libcamera's
|
|
|
|
|
|
`CameraManager` (idempotent), and must succeed before any `*CReq` runs.
|
|
|
|
|
|
|
2026-06-11 23:05:17 -04:00
|
|
|
|
### Device acquisition
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
```cpp
|
2026-06-11 23:05:17 -04:00
|
|
|
|
struct LcameraDevGetOrCreateResult
|
|
|
|
|
|
{
|
2026-06-13 12:02:04 -04:00
|
|
|
|
std::shared_ptr<CameraSession> deviceSession;
|
|
|
|
|
|
CameraIdentityRecord resolvedIdentity;
|
2026-06-11 23:05:17 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
typedef sscl::co::ViralNonPostingInvoker<LcameraDevGetOrCreateResult>
|
|
|
|
|
|
lcameraDev_getOrCreateDeviceCReqFn(const std::string& deviceSelector);
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
typedef sscl::co::ViralNonPostingInvoker<void>
|
|
|
|
|
|
lcameraDev_releaseDeviceCReqFn(
|
|
|
|
|
|
const std::shared_ptr<CameraSession>& deviceSession);
|
2026-06-11 23:05:17 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
Failures throw `std::exception`. `lcameraBuff` holds the returned
|
|
|
|
|
|
`shared_ptr<CameraSession>` and passes it back to `releaseDeviceCReq`. The
|
2026-06-13 20:56:33 -04:00
|
|
|
|
session wraps the acquired `libcamera::Camera`; higher layers configure the mode
|
|
|
|
|
|
then stream from that handle.
|
|
|
|
|
|
|
|
|
|
|
|
### Session mode configuration
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
enum class LcameraDevColourSpace { Yuv };
|
|
|
|
|
|
|
|
|
|
|
|
struct LcameraDevCameraModeRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned width = 0;
|
|
|
|
|
|
unsigned height = 0;
|
|
|
|
|
|
LcameraDevColourSpace colourSpace = LcameraDevColourSpace::Yuv;
|
|
|
|
|
|
bool fullPlanarIsOptional = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct LcameraDevConfiguredCameraMode
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned width;
|
|
|
|
|
|
unsigned height;
|
|
|
|
|
|
LcameraDevColourSpace colourSpace;
|
|
|
|
|
|
std::string pixelFormatName;
|
|
|
|
|
|
bool isFullyPlanar;
|
|
|
|
|
|
unsigned planeCount;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef sscl::co::ViralNonPostingInvoker<LcameraDevConfiguredCameraMode>
|
|
|
|
|
|
lcameraDev_configureSessionModeCReqFn(
|
|
|
|
|
|
const std::shared_ptr<CameraSession>& deviceSession,
|
|
|
|
|
|
const LcameraDevCameraModeRequest& request);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`lcameraDev_configureSessionModeCReq` delegates to
|
|
|
|
|
|
`CameraSession::configureSessionModeCReq`, which runs libcamera
|
|
|
|
|
|
`generateConfiguration` + `configure` and stores the result on the session.
|
|
|
|
|
|
Identical reconfigure is a no-op; conflicting reconfigure throws.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
### Enumeration (discovery)
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
```cpp
|
2026-06-11 23:05:17 -04:00
|
|
|
|
struct LcameraDevCameraInfo
|
|
|
|
|
|
{
|
2026-06-13 12:02:04 -04:00
|
|
|
|
std::string id;
|
|
|
|
|
|
std::string model;
|
|
|
|
|
|
std::string location;
|
2026-06-11 23:05:17 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
typedef sscl::co::ViralNonPostingInvoker<std::vector<LcameraDevCameraInfo>>
|
|
|
|
|
|
lcameraDev_enumerateCamerasCReqFn(void);
|
2026-06-11 23:05:17 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
### Manual verification tools
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
When built with `-DENABLE_LIB_lcameraDev=ON`:
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
* `lcameraDev_list_cameras` — runs `enumerateCamerasCReq` on a minimal probe
|
|
|
|
|
|
`ComponentThread`.
|
|
|
|
|
|
* `lcameraDev_probe <deviceSelector>` — `getOrCreateDeviceCReq`, then
|
|
|
|
|
|
`releaseDeviceCReq` (selector and session attach/detach only).
|
2026-06-13 20:56:33 -04:00
|
|
|
|
* `lcameraDev_configure_probe <deviceSelector> <width> <height> [options]` —
|
|
|
|
|
|
`getOrCreateDeviceCReq`, `configureSessionModeCReq`, print resolved mode (or
|
|
|
|
|
|
exception), then `releaseDeviceCReq`. Options: `--colour-space=yuv`,
|
|
|
|
|
|
`--opt-planar` / `--full-planar-is-optional`, `--reconfigure-twice`.
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
## Module layout
|
2026-06-11 23:05:17 -04:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
commonLibs/lcameraDev/
|
|
|
|
|
|
CMakeLists.txt
|
2026-06-13 12:02:04 -04:00
|
|
|
|
lcameraDev.h / lcameraDev.cpp Public C API and dlopen exports
|
|
|
|
|
|
cameraManagerState.h / .cpp CameraManager singleton, session map
|
|
|
|
|
|
cameraSession.h / .cpp Refcounted acquired-camera session
|
|
|
|
|
|
cameraIdentity.h / .cpp Discovery identity records
|
|
|
|
|
|
selectorParse.h / .cpp Compound selector parsing
|
|
|
|
|
|
selectorResolve.h / .cpp AND-match resolution
|
2026-06-13 20:56:33 -04:00
|
|
|
|
tools/ lcameraDev_list_cameras, lcameraDev_probe,
|
|
|
|
|
|
lcameraDev_configure_probe
|
2026-06-11 23:05:17 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Build links against `libcamera` (pkg-config). Does **not** link Salmanoff
|
|
|
|
|
|
`smocore`; stays a standalone `.so` loadable by `lcameraBuff` and test tools.
|
|
|
|
|
|
|
|
|
|
|
|
## Relationship to future work
|
|
|
|
|
|
|
|
|
|
|
|
| Component | Responsibility |
|
|
|
|
|
|
|---|---|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
| `lcameraDev` | libcamera lifecycle, selector resolution, refcounted acquired camera session |
|
|
|
|
|
|
| `lcameraBuff` | Stream setup, frames, `StimBuffApiDesc`, channel fan-out, intrins |
|
2026-06-11 23:05:17 -04:00
|
|
|
|
| `rasterStimulus` (future) | YUV↔HSV, plane extraction, threshold masks, stencil geometry |
|
|
|
|
|
|
| `xcbWindow` / `waylandWindow` | Separate capture path; reuse `rasterStimulus` only |
|
|
|
|
|
|
|
|
|
|
|
|
If libcamera IDs prove insufficient in practice, selector policies can gain
|
|
|
|
|
|
`by-id:` / `serial:` fallbacks that scan udev/USB metadata without changing the
|
|
|
|
|
|
`lcameraBuff` DAP surface — only `lcameraDev` selector resolution changes.
|
|
|
|
|
|
|
|
|
|
|
|
## Open questions
|
|
|
|
|
|
|
2026-06-13 12:02:04 -04:00
|
|
|
|
1. **Hot-unplug** — on camera removal, fail all attached `lcameraBuff` producers
|
2026-06-11 23:05:17 -04:00
|
|
|
|
and drop the session, or attempt re-enumeration by stored `lcamera-id:`?
|
2026-06-13 12:02:04 -04:00
|
|
|
|
2. **IPA packaging** — document per-platform `apt install` requirements in the
|
2026-06-11 23:05:17 -04:00
|
|
|
|
main README when `lcameraBuff` lands (RPi needs `libcamera-ipa`).
|
2026-06-13 12:02:04 -04:00
|
|
|
|
|
|
|
|
|
|
Stream format, frame timing, and libcamera callback threading are owned by
|
|
|
|
|
|
`lcameraBuff`, not `lcameraDev`.
|