lcameraDev: Add session mgr lib for libcamera device binding
This commit is contained in:
+66
-61
@@ -34,8 +34,10 @@ lcameraBuff (stimBuffApi) SMO-facing producers, channel fan-out, intrins
|
||||
|
||||
Channel splitting, colourspace conversion, threshold masks, and stencils belong
|
||||
in a separate shared raster library (`rasterStimulus`, future) and in
|
||||
`lcameraBuff`; `lcameraDev` stops at “here is a stable, acquired libcamera
|
||||
`Camera` you can configure and stream from.”
|
||||
`lcameraBuff`; `lcameraDev` stops at selector resolution, `CameraManager`
|
||||
lifecycle, and a refcounted, **acquired** `libcamera::Camera` handle per
|
||||
resolved device. Stream negotiation, pixel-format selection, frame buffers,
|
||||
and capture timing belong in `lcameraBuff` (and supporting libraries), not here.
|
||||
|
||||
## Why libcamera (for now)
|
||||
|
||||
@@ -120,7 +122,8 @@ Possible future params:
|
||||
|
||||
* `fps-hz=30`
|
||||
* `width=640|height=480`
|
||||
* `pixfmt=NV12` — negotiation hint passed down to `lcameraDev`
|
||||
* `pixfmt=NV12` — negotiation hint for `lcameraBuff` stream setup (not
|
||||
`lcameraDev`)
|
||||
|
||||
## Device selector format
|
||||
|
||||
@@ -180,7 +183,7 @@ tokens).
|
||||
|
||||
Resolution algorithm (summary):
|
||||
|
||||
1. Start `CameraManager` (once per process, inside `lcameraDev`).
|
||||
1. Start `CameraManager` in `lcameraDev_main` (once per process).
|
||||
2. Enumerate cameras; build an identity record per camera (`id`, `model`,
|
||||
`location`, `systemDevices`).
|
||||
3. Parse `deviceSelector` into criterion clauses; apply **all** clauses (AND).
|
||||
@@ -197,9 +200,9 @@ each camera’s `id()`, `model`, and `location` so operators can copy a stable
|
||||
## Shared session and refcounting
|
||||
|
||||
Unlike X11 windows, a camera has no “sub-objects” inside its feed — the selector
|
||||
always designates the whole camera stream. Multiple DAP lines for H, S, and V
|
||||
are **views** over one capture session, all under the same `dev-identifier`
|
||||
(e.g. `cam0`).
|
||||
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`).
|
||||
|
||||
```text
|
||||
dev-identifier cam0 + deviceSelector (resolved) ──> LcameraDeviceSession (refcounted)
|
||||
@@ -213,11 +216,10 @@ dev-identifier cam0 + deviceSelector (resolved) ──> LcameraDeviceSession (re
|
||||
Rules:
|
||||
|
||||
* First `getOrCreate` for a resolved camera ID: enumerate, `acquire()` the
|
||||
`libcamera::Camera`, negotiate and start the shared stream.
|
||||
`libcamera::Camera`, create the session entry.
|
||||
* Subsequent `getOrCreate` for the same ID: increment refcount; return the same
|
||||
session handle.
|
||||
* Last `release`: `release()` the libcamera camera, stop streaming, tear down
|
||||
buffers.
|
||||
* Last `release`: `release()` the libcamera camera and erase the session entry.
|
||||
* 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
|
||||
@@ -226,84 +228,89 @@ Rules:
|
||||
the same physical device from SMO’s perspective; channel differences come from
|
||||
the qualeIface name on each DAP line.
|
||||
|
||||
## dlopen API (planned)
|
||||
Streaming, frame delivery, and colourspace work are **out of scope** for
|
||||
`lcameraDev`; `lcameraBuff` uses the session’s acquired camera handle to set up
|
||||
capture on attach.
|
||||
|
||||
## dlopen API
|
||||
|
||||
Exported from `liblcameraDev.so` using `extern "C"` symbols (mirroring
|
||||
`livoxProto1`). `lcameraBuff` loads the library with `dlopen` + `dlsym` and
|
||||
calls through function pointers.
|
||||
calls through function pointers. Hot-path operations are **`*CReq` coroutine
|
||||
invokers** (`sscl::co::ViralNonPostingInvoker`); `main` / `exit` remain
|
||||
synchronous.
|
||||
|
||||
Header: `commonLibs/lcameraDev/lcameraDev.h`.
|
||||
|
||||
### Lifecycle
|
||||
|
||||
```c
|
||||
/* Start CameraManager; idempotent per process. */
|
||||
typedef void lcameraDev_mainFn(void);
|
||||
typedef void lcameraDev_mainFn(
|
||||
const std::shared_ptr<sscl::ComponentThread>& componentThread);
|
||||
|
||||
/* Stop manager, release all devices; called on SMO shutdown. */
|
||||
typedef void lcameraDev_exitFn(void);
|
||||
```
|
||||
|
||||
`lcameraDev_main` records the Body `ComponentThread`, starts libcamera's
|
||||
`CameraManager` (idempotent), and must succeed before any `*CReq` runs.
|
||||
|
||||
### Device acquisition
|
||||
|
||||
```c
|
||||
```cpp
|
||||
struct LcameraDevGetOrCreateResult
|
||||
{
|
||||
bool success;
|
||||
/* Opaque session handle; valid until matching release call. */
|
||||
void* deviceSession;
|
||||
/* Resolved libcamera camera ID (stable session key). */
|
||||
const char* resolvedCameraId;
|
||||
std::shared_ptr<CameraSession> deviceSession;
|
||||
CameraIdentityRecord resolvedIdentity;
|
||||
};
|
||||
|
||||
typedef LcameraDevGetOrCreateResult lcameraDev_getOrCreateDeviceFn(
|
||||
const char* deviceSelector);
|
||||
typedef sscl::co::ViralNonPostingInvoker<LcameraDevGetOrCreateResult>
|
||||
lcameraDev_getOrCreateDeviceCReqFn(const std::string& deviceSelector);
|
||||
|
||||
typedef void lcameraDev_releaseDeviceFn(void* deviceSession);
|
||||
typedef sscl::co::ViralNonPostingInvoker<void>
|
||||
lcameraDev_releaseDeviceCReqFn(
|
||||
const std::shared_ptr<CameraSession>& deviceSession);
|
||||
```
|
||||
|
||||
`deviceSession` is opaque to callers. `lcameraBuff` passes it back when
|
||||
detaching; it must not call libcamera APIs directly.
|
||||
Failures throw `std::exception`. `lcameraBuff` holds the returned
|
||||
`shared_ptr<CameraSession>` and passes it back to `releaseDeviceCReq`. The
|
||||
session wraps the acquired `libcamera::Camera`; higher layers configure and
|
||||
stream from that handle — `lcameraDev` does not expose frame or stream APIs.
|
||||
|
||||
### Enumeration (discovery)
|
||||
|
||||
```c
|
||||
```cpp
|
||||
struct LcameraDevCameraInfo
|
||||
{
|
||||
const char* id;
|
||||
const char* model; /* empty string if unavailable */
|
||||
const char* location; /* "front", "back", "external", or "" */
|
||||
std::string id;
|
||||
std::string model;
|
||||
std::string location;
|
||||
};
|
||||
|
||||
struct LcameraDevEnumerateResult
|
||||
{
|
||||
size_t count;
|
||||
LcameraDevCameraInfo* cameras; /* caller frees via lcameraDev_freeEnumerateResult */
|
||||
};
|
||||
|
||||
typedef LcameraDevEnumerateResult lcameraDev_enumerateCamerasFn(void);
|
||||
typedef void lcameraDev_freeEnumerateResultFn(LcameraDevEnumerateResult* result);
|
||||
typedef sscl::co::ViralNonPostingInvoker<std::vector<LcameraDevCameraInfo>>
|
||||
lcameraDev_enumerateCamerasCReqFn(void);
|
||||
```
|
||||
|
||||
### Frame access (boundary with lcameraBuff)
|
||||
### Manual verification tools
|
||||
|
||||
Exact frame-delivery API is TBD and will likely use a callback or a “dequeue
|
||||
latest frame buffer” method on the session handle. `lcameraDev` owns libcamera
|
||||
`FrameBuffer` allocation and `Request` requeue; `lcameraBuff` copies or maps the
|
||||
plane it needs for the active qualeIface channel.
|
||||
When built with `-DENABLE_LIB_lcameraDev=ON`:
|
||||
|
||||
Principle: **one negotiated stream format per session** (prefer native YUV such
|
||||
as NV12/YUYV). `lcameraBuff` + `rasterStimulus` derive H/S/V greyscale planes
|
||||
from that single stream.
|
||||
* `lcameraDev_list_cameras` — runs `enumerateCamerasCReq` on a minimal probe
|
||||
`ComponentThread`.
|
||||
* `lcameraDev_probe <deviceSelector>` — `getOrCreateDeviceCReq`, then
|
||||
`releaseDeviceCReq` (selector and session attach/detach only).
|
||||
|
||||
## Module layout (planned)
|
||||
## Module layout
|
||||
|
||||
```text
|
||||
commonLibs/lcameraDev/
|
||||
CMakeLists.txt
|
||||
lcameraDev.h Public C API + C++ internal headers
|
||||
lcameraDev.cpp dlopen exports, CameraManager singleton
|
||||
cameraSession.cpp Refcounted session, stream negotiation
|
||||
selectorResolve.cpp deviceSelector parsing and matching
|
||||
cameraEnumerate.cpp Discovery / identity records
|
||||
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
|
||||
tools/ lcameraDev_list_cameras, lcameraDev_probe
|
||||
```
|
||||
|
||||
Build links against `libcamera` (pkg-config). Does **not** link Salmanoff
|
||||
@@ -313,8 +320,8 @@ Build links against `libcamera` (pkg-config). Does **not** link Salmanoff
|
||||
|
||||
| Component | Responsibility |
|
||||
|---|---|
|
||||
| `lcameraDev` | libcamera lifecycle, selector resolution, shared capture session |
|
||||
| `lcameraBuff` | `StimBuffApiDesc`, `StimulusProducer`, per-channel ring buffers, intrins |
|
||||
| `lcameraDev` | libcamera lifecycle, selector resolution, refcounted acquired camera session |
|
||||
| `lcameraBuff` | Stream setup, frames, `StimBuffApiDesc`, channel fan-out, intrins |
|
||||
| `rasterStimulus` (future) | YUV↔HSV, plane extraction, threshold masks, stencil geometry |
|
||||
| `xcbWindow` / `waylandWindow` | Separate capture path; reuse `rasterStimulus` only |
|
||||
|
||||
@@ -324,12 +331,10 @@ If libcamera IDs prove insufficient in practice, selector policies can gain
|
||||
|
||||
## Open questions
|
||||
|
||||
1. **Stream format defaults** — prefer first supported YUV format, or allow
|
||||
`lcameraBuff(pixfmt=...)` override?
|
||||
2. **Hot-unplug** — on camera removal, fail all attached `lcameraBuff` producers
|
||||
1. **Hot-unplug** — on camera removal, fail all attached `lcameraBuff` producers
|
||||
and drop the session, or attempt re-enumeration by stored `lcamera-id:`?
|
||||
3. **Thread model** — libcamera callbacks vs polling in the Body thread
|
||||
production daemon; align with `StimulusProducer::productionCDaemon` timing
|
||||
(`CONFIG_STIMBUFF_FRAME_PERIOD_MS`).
|
||||
4. **IPA packaging** — document per-platform `apt install` requirements in the
|
||||
2. **IPA packaging** — document per-platform `apt install` requirements in the
|
||||
main README when `lcameraBuff` lands (RPi needs `libcamera-ipa`).
|
||||
|
||||
Stream format, frame timing, and libcamera callback threading are owned by
|
||||
`lcameraBuff`, not `lcameraDev`.
|
||||
|
||||
Reference in New Issue
Block a user