Skip to content

wl-record-pixelflux

Recipe card from the charly-selkies plugin (Images — the deployable catalog).

wl-record-pixelflux – Desktop video recording via selkies capture bridge

Section titled “wl-record-pixelflux – Desktop video recording via selkies capture bridge”
Property Value
Install files charly.yml, task:, pixelflux-record (Python script)
Depends selkies (capture bridge + WebSocket stream), ffmpeg (MP4 muxing)

Provides pixelflux-record for recording desktop video on selkies-desktop. Connects to the capture bridge at /tmp/charly-capture.sock which relays the same H.264 frames that the browser sees via the selkies WebSocket stream. No direct ScreenCapture API access – taps into the existing selkies streaming pipeline to avoid creating a second capture instance (the Rust backend only supports one active capture at a time).

Why not wf-recorder? labwc running nested inside pixelflux can’t deliver wlr-screencopy frames. The capture bridge bypasses this by tapping into the selkies WebSocket stream directly.

  1. Connects to Unix socket at /tmp/charly-capture.sock (started by selkies-capture-server)
  2. Sends STREAM\n to request continuous H.264 frame data
  3. Receives frames as 4-byte length + raw H.264 data pairs
  4. Pipes raw H.264 data to ffmpeg with -use_wallclock_as_timestamps 1
  5. ffmpeg re-encodes to MP4 with correct wall-clock timing
  6. SIGINT/SIGTERM triggers graceful shutdown: closes socket, waits for ffmpeg to finalize MP4
Property Value
Depends selkies (capture bridge + stream), ffmpeg (muxing)
Install ~/.local/bin/pixelflux-record (Python script)
Capture Via /tmp/charly-capture.sock (selkies WebSocket bridge)
Output MP4 (H.264 video + optional AAC audio)
Audio PulseAudio monitor source via ffmpeg
Terminal window
# Direct usage
pixelflux-record output.mp4 # 30fps, video only
pixelflux-record output.mp4 --fps 60 # 60fps
pixelflux-record output.mp4 --audio # video + audio
pixelflux-record output.mp4 --fps 60 --audio # 60fps + audio
# Stop with Ctrl-C

Author record: plan steps (the declarative verb served out-of-process by candy/plugin-record — no host charly check subcommand for it) and run them with charly check live selkies-desktop --filter record. A record: start step with record_mode: desktop + record_audio: true auto-detects pixelflux-record; desktop interaction is driven with the cdp:/wl: verbs (which keep their host subcommands); record: stop + artifact: copies the .mp4 out:

pixelflux-rec-start:
check: a desktop recording with audio starts
record: start
context: [deploy]
record_name: demo
record_mode: desktop
record_audio: true
pixelflux-rec-stop:
check: the desktop recording is captured
record: stop
context: [deploy]
record_name: demo
artifact: demo.mp4
artifact_not_uniform: true
selkies process (single ScreenCapture singleton — process-wide)
├── ScreenCapture (captures full composited desktop)
├── WebSocket server :8081 (broadcasts H.264 frames)
└── Capture bridge thread (internal WebSocket client)
└── Unix socket /tmp/charly-capture.sock
└── pixelflux-record connects here (STREAM mode)
└── pipes H.264 frames to ffmpeg

Singleton note: The ScreenCapture instance is process-wide and lives inside the selkies Python process. pixelflux-record never spawns its own capture; it only attaches to the existing STREAM socket. This is architecturally important because pixelflux’s WaylandBackend construction is expensive (EGL context + dmabuf allocators + GPU texture pools) and was the subject of a memory leak fix in commits 6be85eb (singleton enforcement) and 7977b91 (per-frame cleanup_texture_cache()). If you ever see two selkies-capture processes inside the container, that’s a regression — see /charly-selkies:selkies (Pixelflux Memory Management) for the diagnostic recipe.

  • selkies-desktop metalayer

Use when the user asks about:

  • Desktop video recording on selkies-desktop
  • The pixelflux recording pipeline
  • The wl-record-pixelflux candy
  • /charly-image:layer — candy authoring reference (charly.yml schema, task verbs, service declarations)
  • /charly-check:check — declarative testing (check: block, charly check box, charly check live)