Expand description
M-CAM.3 / AUT-257 — camera-pipeline worker thread.
Owns a dedicated OS thread that runs the gst-autovideosrc capture
subprocess, pulls BGRA frames into Rust, and (in follow-up commits)
uploads each frame to a wisp::VideoTexture, renders the wisp
scene with an M-VEC.6 circle mask into an offscreen
RenderTexture, reads back the masked BGRA bytes, and emits them
to Leptos via a Tauri Channel<T>.
Just the **gst-into-Rust** layer:
1. `start_preview` spawns a [`CameraPipeline`] worker.
2. Worker opens `media::gstreamer_video::VideoStream::from_default_camera`,
triggering the macOS permission prompt on first run.
3. Worker loops `next_frame()` and advances the
[`PreviewLifecycle`](crate::preview::PreviewLifecycle) state
machine — `Starting → Running` on first successful frame.
4. `stop_preview` drops the worker; `Drop` flips the cancel flag
and joins the thread. The gst child is killed by
`gstreamer_video::VideoStream`'s own `Drop` impl (per CLAUDE.md
"Drop-kill the child" pattern).
NOT yet shipped:
* **No wisp upload + render** — the frames sit in the worker; they
don't yet flow through a `wisp::Stage` + M-VEC.6 mask. That's the
next commit.
* **No frame emission to Leptos** — Tauri `Channel<T>` is wired
alongside the wisp work. For now, the user sees the `Running`
lifecycle transition in `preview_status` but no pixels.Thread-affinity contract — media::gstreamer_video::VideoStream
owns a std::process::Child (gst-launch subprocess) + a stdout
reader. Child is Send, so the worker thread can own the stream
exclusively. No Rc/RefCell anywhere in the type, so it’s safe
to move into the spawned thread.
Structs§
- Camera
Pipeline - Camera-pipeline worker handle. Owns the spawned thread and a
cooperative cancel flag;
Dropcancels + joins so a panicking caller can never leave a zombie gst child behind. - Camera
Pipeline Handle - Tauri-managed handle for the active camera pipeline. Held in
tauri::Stateso thestop_previewcommand can drop the worker (and consequently kill the gst child + join the thread).
Constants§
- PREVIEW_
FPS - Source framerate request. gst will negotiate the closest the OS
camera supports; the actual rate is reflected in
[
media::gstreamer_video::GstreamerVideoCapture::framerate]. - PREVIEW_
HEIGHT - Default capture height in pixels. Matches
PREVIEW_WIDTH— see that constant’s docs for the square-crop rationale. - PREVIEW_
WIDTH - Default capture width in pixels. Paired with
PREVIEW_HEIGHT— square dims are the natural input shape for the circular bubble mask. 720×720 (M-QUAL.3): the webcam’s native 16:9 frame is center-cropped to 1:1 then scaled here, so the recorded bubble is crisp at native-resolution output instead of an upscaled 480².
Functions§
- advance_
to_ 🔒running - Mark the preview lifecycle as
Running(idempotent — already-Runningstays running). Called from the worker thread on each successful frame; themark_runningtransition is idempotent so we don’t need a “first frame” guard. - reset_
lifecycle 🔒 - Drive the lifecycle back to
Idleon shutdown OR on gst-side startup failure (no camera attached, permission denied, etc.). - run_
pipeline 🔒 - The actual worker loop. Lives on the spawned thread; the only
reason it’s
&borrows is so the public-facingspawncan move the cloned values in without keepingSelfalive on the thread.