Skip to main content

run_capture

Function run_capture 

Source
pub fn run_capture<F>(
    settings: RustCaptureSettings,
    controls: Arc<Controls>,
    encode_tid_tx: Sender<ThreadId>,
    on_frame: F,
) -> Result<(), String>
where F: FnMut(Vec<EncodedStripe>) + Send + 'static,
Expand description

Run the X11 capture pipeline until stop is set, splitting capture and encode across two threads that overlap for throughput.

This (the caller’s) thread performs setup and then the capture loop; a spawned encode thread runs encode_loop and invokes on_frame(stripes) once per encoded frame. The split lets capture and encode overlap, and because frames are throttled/dropped as RAW frames before encode, the delivered H.264 stays a valid contiguous reference chain.

  1. Setup: connect to X (a private connection) and require a 32-bpp BGRA root — the Z-pixmap byte depth must be 4, which modern servers use for depth 24/32 — then negotiate XShm and XFixes. XFixes is always negotiated (one round-trip) so the cursor overlay can be toggled on live even when capture started without it. Initial dimensions and origin are resolved from the settings and the live root geometry, and a FramePool of POOL_N = 3 shm surfaces is allocated — the working set (one in-capture, one in-slot, one in-encode) that a one-frame-ahead demand-driven capture needs, which also keeps the memory cost (3 * W*H*4, significant at 4K) bounded.
  2. Encode thread: spawned with a raised scheduling priority; it reports its thread id back through encode_tid_tx so the caller can detect a re-entrant stop issued from inside the delivery callback.
  3. Capture loop (until stop): fps is re-read each iteration for live pacing — sleep to the next frame deadline, or yield_now when already behind instead of busy-spinning.
    • Live region change (region_dirty): re-target the grab origin immediately (an x/y pan needs no surface work); a size change reuses the drain/recreate path below.
    • Auto-adjust: on a root geometry change, drain in-flight frames then recreate the surfaces and bump the generation (which the encode thread turns into a reshape or rebuild). The geometry is re-resolved AFTER the drain, because a slow drain can outlast another geometry change (a fast flap) and recreating at a stale size would make the next shm_get_image exceed the root; a fully-reverted flap keeps its surfaces as-is. A stop that races the drain breaks out to teardown.
    • Grab: acquire a pooled surface (blocks until the encoder frees one), then shm_get_image the region into it synchronously (reply() waits).
    • Overlays: composite the XFixes cursor (drawn at its hotspot-offset origin; live-toggleable) and the watermark onto the BGRA pixels on the CPU.
    • Publish: hand the finished frame to the encode thread (blocks until the slot is free; never drops). A stop observed while waiting in acquire/publish exits the loop.
  4. Teardown: stop and JOIN the encode thread BEFORE destroying the shm surfaces it may still be reading, preserving the resize-safety guarantee.

Blocking; intended to run on a dedicated thread. The X connection and shm surfaces live on this thread and the encoder lives on the encode thread — nothing X-related crosses the boundary.