Skip to main content

Module recorder

Module recorder 

Source
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_capture instance 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 RequestIdr command); 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 OpusHead the 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§

ActiveRecording 🔒
IdrRequester 🔒
Sends ThreadCommand::RequestIdr for 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.
RecordOptions
Resolved recording parameters. Environment variables provide the defaults; explicit Python/REST arguments override them.
RecordingStatus
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§

PreferredBackend
Which capture system feeds the recording.
RecordingMode 🔒
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 (via DISPLAY) 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 OpusHead the 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; None when 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 Arc into 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.