Expand description
Built-in MP4 recorder: independent capture-to-file with API/env/REST control. Built-in MP4 recorder: an independent pipeline that captures the desktop to a ready-to-play fragmented MP4 without needing any connected client.
One implementation serves three control surfaces — the Python module functions
(start_recording / stop_recording / recording_status), the PIXELFLUX_RECORD*
environment variables (record from process start), and the record_start /
record_stop / record_status commands of crate::computer_use — so their
behavior cannot drift.
The recorder is a consumer of the existing capture machinery, never a hook inside an encoder:
- X11: it always runs its own
crate::x11::run_captureinstance against the root window (a second capture of the same root is independent of any streaming session), with the encoded frames delivered straight into the recorder’s queue. - Wayland: the compositor lives in this process and allows one capture per output, so
the recorder attaches at the delivery layer. When the output is already being captured
for a streaming client it taps that stream (and paces recovery keyframes through the
standard
RequestIdrcommand); when the output is idle it starts its own capture with no Python callback and taps the identical delivery point.
Frames cross into the recorder through one bounded queue, mirroring the Unix-socket sink’s
isolation contract: the delivery thread only clones an Arc and try_sends, an overflowing
queue drops frames (never blocks the pipeline), and all muxing happens on the recorder’s own
writer thread. Timestamps are wall-clock, so the damage-driven, variable-rate frame flow
lands at its true times in the MP4.
Sound comes from an Ogg Opus stream on a Unix socket, the shape pcmflux serves, read by a
thread of its own: the OpusHead declares the track, and each packet’s decode time is
its place under its page’s granule position, laid onto the recording clock from the first
packet’s arrival.
Modules§
- mp4
- Pure-Rust fragmented-MP4 (fMP4) muxer for the built-in recorder.
- ogg
- Reader for the Ogg Opus stream an audio sink serves: pages are taken as they arrive
and split into packets, a packet continued across pages reassembled. The first packet
is the
OpusHeadthe audio track is declared from; every later packet comes with the page’s granule position, the 48 kHz sample count at the end of the last packet the page completes, which is the recorder’s audio clock.
Structs§
- Active
Recording 🔒 - IdrRequester 🔒
- Sends
ThreadCommand::RequestIdrfor one display over the compositor’s command channel. - RecShared 🔒
- Live counters shared between the feeding side (delivery threads), the writer thread, and the status surfaces.
- Record
Options - Resolved recording parameters. Environment variables provide the defaults; explicit Python/REST arguments override them.
- Recording
Status - Point-in-time view of the recorder, identical across the Python, env, and REST surfaces.
- WlTap 🔒
- The wayland-side tap handle consulted by
wayland_tap; cloneable so the delivery thread can drop the registry lock before doing any work.
Enums§
- Preferred
Backend - Which capture system feeds the recording.
- Recording
Mode 🔒 - How the active recording is fed and what must be torn down on stop.
- Tap 🔒
- What crosses into the writer: a video frame as its
Arc-shared encoded payload, the byte offset where the Annex-B stream starts (past the wire header when present), and the wall-clock capture time; or an audio packet with its decode time on the 48 kHz clock.
Constants§
- EARLY_
AUDIO_ 🔒CAP - Audio packets held before the video’s first IDR opens the file: ten seconds of 20 ms packets, the oldest dropped past that.
- QUEUE_
CAP 🔒 - Recorder queue bound, matching the socket sink’s stalled-consumer policy: a writer that falls this far behind loses frames instead of growing memory or blocking the encoder.
Statics§
- ACTIVE 🔒
- LAST_
FINISHED 🔒 - WL_TAP 🔒
- WL_
TAP_ 🔒ARMED - Fast idle guard for
wayland_tap: the delivery threads pay one relaxed atomic load per frame while no recording is armed.
Functions§
- audio_
thread 🔒 - Audio-thread body: dates each page’s packets from
page_start, laid onto the recording clock from the first packet’s arrival. Packets wait for their page’s last, which carries the granule; an unfinished page’s packets at the stream’s end follow the page before. Ends with the stream, or on stop. - autostart_
from_ env PIXELFLUX_RECORD=<path>: record from process start. X11 (viaDISPLAY) starts immediately; otherwise a background thread waits for the in-process Wayland compositor to come up and then starts. Retries transient failures until the deadline.- disarm_
tap 🔒 - finalize_
on_ exit - Finalize any active recording at interpreter shutdown so the MP4’s last buffered sample is flushed. Best-effort; the fragmented layout keeps even an unflushed file playable.
- offer_
frame 🔒 - Enqueue one delivered frame for muxing. Only H.264 is recordable; JPEG or another codec is counted and skipped so the surfaces can report a clear “nothing recordable” error instead of writing a corrupt file.
- open_
audio 🔒 - Connect to the audio sink within a moment and read its headers: the reader positioned
at the first audio packet, and the
OpusHeadthe track is declared from. - open_
recording 🔒 - Open the recording’s file. The recording is the desktop: readable by its owner alone, never written through a link someone else may have planted at a predictable name in a shared directory, nor into a file there that is another’s, or has another name too.
- own_
capture_ 🔒settings - Capture settings for a recorder-owned capture: explicit settings when given (validated H.264), otherwise derived defaults, always forced to the one recordable shape (full-frame H.264, no socket rebind, scheduled recovery keyframes).
- pace_
idr_ 🔒requests - Issue a standard request-IDR when the cadence interval has elapsed. Compare-and-swap on the last-request stamp so concurrent delivery threads emit one request per interval.
- page_
start 🔒 - Where a page’s first packet starts, in samples: its granule position counts to the end of
its last packet, so the page starts what its packets hold (
samples) before that. A producer that sends nothing through a silence (pcmflux’s silence gate) leaves the gap before the page, which then lands after it rather than against the page before; a final page whose granule trims its end starts where the one before ended. - start
- Start a recording. Exactly one may be active per process; returns the initial status.
- status
- The current status: the live recording when one is active, otherwise the last finished
one;
Nonewhen the process has never recorded. - status_
to_ json - Serialize a status into the JSON shape shared by the REST endpoints.
- stop
- Stop the active recording, finalize the MP4, and return the final status. Errors when no recording is active or when nothing recordable was ever received.
- wayland_
tap 🔒 - Delivery-layer feed from the Wayland pipelines (readback encode loop and zero-copy
tick). Near-zero cost when idle; while recording it clones an
Arcinto the bounded queue and never blocks. - writer_
thread 🔒 - Writer-thread body: drain the queue, convert Annex-B to AVCC, and mux. Output starts at the first IDR with parameter sets; earlier frames are discarded and audio packets held. Runs until every sender is dropped (stop) or a write error occurs, then finalizes the file and publishes the final status.