Hyprland's portal sometimes creates the session and never answers source selection, before any picker appears. Recording now fails if the portal has not reached its picker within 10 s; time in the picker is not limited. The motion check uses the segment with the most motion, since capture can start late.
9.0 KiB
Recording foundation validation
The first recording milestone proves the OBS capture, encoder and segment
pipeline before the UI is built (SPEC.md, "Implementation validation before
building out the UI"). clipbin-probe drives the real clipbin-obs engine and
prints key=value lines; the scripts in scripts/validate/ turn them into
pass/fail results.
| Where | Script | Status |
|---|---|---|
| Linux, headless rig (Wayland portal + Xwayland) | scripts/validate/linux-rig.sh |
All 9 scenarios pass on devbox (Intel UHD 770, VA-API) |
| Linux, real session (CachyOS, Hyprland, RX 9070 XT, 1440p60) | scripts/validate/linux-hardware.sh |
2026-10-08: H.264, HEVC and AV1 usable; restore token works; portal stall fix awaiting re-run |
| Windows (Game Capture, NVENC/AMF/QSV) | scripts/validate/windows.ps1 |
Not run yet; script untested |
Probe commands
cargo run -p clipbin-probe -- encoders --verify # hardware encoders per codec, proven by encoding
cargo run -p clipbin-probe -- record --target monitor # records; prints status, health and segments
cargo run -p clipbin-probe -- check # keyframes, codec, durations, cross-boundary remux
record targets monitor, window or game (Game Capture on Windows; the
same as window elsewhere). On Windows and X11, --window takes OBS's window
identifier; on Wayland the portal picker chooses and --restore-token reuses
an earlier choice. See the top of crates/clipbin-probe/src/main.rs for all
options. check needs FFmpeg's ffprobe and ffmpeg on PATH.
Linux headless rig
scripts/rig/headless.sh [--x11] <command> runs a command inside a private
D-Bus session with PipeWire, a virtual audio sink, headless Sway on the GPU,
xdg-desktop-portal-wlr (auto-selecting the only output) and a 60 FPS test
pattern with a tone. --x11 runs the game and command on Sway's Xwayland.
Arch packages: sway xdg-desktop-portal xdg-desktop-portal-wlr xorg-xwayland xorg-xwininfo pipewire pipewire-pulse wireplumber libpulse ffmpeg plus a VA-API
driver (intel-media-driver or libva-mesa-driver).
The suite covers:
- Encoder verification: H.264 must be usable; HEVC and AV1 are reported.
- Wayland monitor capture in H.264 and HEVC: segments start on keyframes, last 2 s, remux to MP4 across boundaries, decode cleanly, carry the tone.
- X11 window capture (Xcomposite).
- Failures that must end in
status=failed: PipeWire stream lost, captured X11 window closed, segment directory unwritable, free space below the margin. - A portal that refuses the request fails with an actionable reason and
never reports
Recording.
Segment checks run with --expect-motion, so black or frozen footage (which
still makes valid segments) fails.
Linux hardware sign-off
Run scripts/validate/linux-hardware.sh from the Hyprland session. It verifies
encoders at 1440p60, records the monitor with each usable codec (the first run
shows the portal picker; later runs pass the restore token and ask whether the
picker stayed away), records a window, and asks you to close a window
mid-recording. Send back report.txt from the output directory.
First hardware run (2026-10-08)
CachyOS 7.2.9, Hyprland, RX 9070 XT, 1440p60, OBS 32.0.4:
- H.264, HEVC and AV1 all encode through VA-API (
*_ffmpeg_vaapi_tex). Monitor and window capture recorded keyframe-aligned 2 s segments that remuxed across boundaries with no decode errors. - The restore token came back on one of three runs, probably because the "Allow a restore token" box in the picker was ticked only once. The script now has a dedicated restore step.
- One run never got a portal selection: the picker either did not appear or
was not answered, and the probe stayed in
Waiting for capturefor 20 s while writing black segments thatcheckpassed.check --expect-motionnow catches black footage; a refused request now fails at once. - Closing the captured window (Super+W) is not visible to capture: xdph keeps
the PipeWire stream
streamingand OBS repeats the window's last frame. See findings.
Second hardware run (2026-10-08)
- The restore token works: the restored run started recording in 2 s without showing the picker.
- The portal stalled silently on one run again: it created the session but
never answered
SelectSources, before any picker. Recording now fails if the portal has not reached its picker 10 s after the request (StatusTracker::PORTAL_RESPONSE_LIMIT); time spent in the picker itself is not limited. - The motion check sampled a segment from before capture started (the user took 16 s in the picker); it now uses the segment with the most motion.
- A portrait monitor (Hyprland transform 3) is captured as 1440x2560 and pillarboxed in the 2560x1440 canvas.
Windows
Run scripts/validate/windows.ps1 -GameWindow "<title>:<class>:<exe>" with a
game running. It assembles the official OBS 32.0.4 runtime, checks the Game
Capture hook signatures, builds the probe, verifies NVENC/AMF/QSV encoders and
records Monitor, Game and Window Capture.
Windows status:
clipbin-core,clipbin-obsandclipbin-probepasscargo clippyforx86_64-pc-windows-msvc(cross-checked from Linux). Nothing has been linked or run on Windows.- The GPUI desktop crate has not been checked for Windows; its
ringdependency needs the MSVC C toolchain. - The assembled runtime keeps the six Game Capture hook binaries byte-identical to the release, with their Authenticode signatures present; signature validity has not been checked on Windows.
- Unvalidated: encoder ids and settings for NVENC, AMF and QSV; Game Capture
hooking and its loss detection (the engine expects the source size to drop
to 0); WASAPI audio;
obs-ffmpeg-mux.exebeside the executable; linkingdstr_vprintfthrough the libobs crate'sobs.lib.
Findings for the next milestones
- Plugin loading. libobs always adds default plugin paths: the build-time
install prefix on Linux and
../../obs-plugins/64bitrelative to the working directory on Windows. Searching would load other OBS installations' plugins or load ours twice, so the engine opens exactly the bundled allowlist (runtime::PLUGINS) by path. - Segment timestamps. The FFmpeg muxer restarts each segment's video and audio timestamps at zero independently. Concatenating segments naively leaves 22–43 ms video gaps at boundaries. Video is constant frame rate and AAC packets are continuous, so a segment's exact session offset is the count of earlier frames and audio packets; the A/V offset stays within one AAC packet (≤21 ms) and does not drift. The timeline and export work must place segments this way.
- Availability. With 1 s keyframes and 2 s segments, splits happen on schedule every 2.0 s, so the newest complete footage is about 2 s behind plus finalisation: inside the spec's 2–5 s window.
- Honest status needs more than OBS's counters. OBS kept counting bytes into a dead muxer process, and once reported a clean stop after the disk filled. Status therefore requires footage to grow on disk, and recording stops while 1 GB remains so the last segment can be finalised.
- PipeWire source loss. OBS's PipeWire capture keeps its size and renders its last frame after the stream stops, and only logs the stream state. The engine reads that log line (stable for the pinned OBS version). A more robust follow-up is watching the PipeWire node state directly.
- Closed windows on Wayland. With Hyprland's portal, closing a captured window leaves the stream running on its last frame, with no state change or error. Capture cannot tell this apart from a static window, so sessions must end from game process exit (SPEC: "Stop/finalise the session when its game exits"), which the Steam/process detection milestone provides.
- Writing while waiting for capture. The output starts before the source has a picture, so a source that never arrives produces black segments. The session timeline should show that time as missing footage, or the output should start only once capture is usable.
- Canvas orientation. The output canvas is fixed by the configured resolution, so portrait or other-aspect sources are letterboxed. Decide whether the canvas should follow the captured source.
- Audio capture failures are not detected yet. When the PulseAudio server was unreachable, recording continued with a silent track.
- Linux runtime dependencies. The Linux OBS build links the system's
FFmpeg, PipeWire and other libraries, so it is tied to the distribution it
was built on. Packaging (for example Flatpak, or bundling FFmpeg) is an open
decision; OBS 32.0 needs mbedtls 3 (
mbedtls3on Arch). - OBS objects. Scenes and sources are created private (OBS 31+ otherwise registers scenes with the main canvas, which keeps them alive), unknown source types fail instead of becoming placeholders, and shutdown drains libobs's destroy queue first.
- Plugin configuration. libobs is started without a module config path; set one under the app's config directory before plugins that save settings are added.