- PROJECT_GUIDE: repository table with forgejo as source of truth and spearzificial as the active GitHub account; explains that the oiupoyt GitHub token is expired and why the username is pinned in the remote URL so Git picks the right credential from the multi-account store - PROJECT_GUIDE: document ShowOverlay and the retired-config table with the reason each key existed without effect - README: point releases, clone URL and CLI examples at the unified binary and the spearzificial repository - TODO_CHECKLIST: add the release pipeline, dead config, ShowOverlay and e2e socket entries; note that pre-v0.2.18 src/bin paths are historical
24 KiB
Scythe: Comprehensive Project & Architecture Guide
This document is the definitive technical reference for developers and AI agents working on the Scythe codebase. It outlines the project's design philosophy, unified binary architecture, hardware encoding pathways, audio subsystem, power management, HUD overlay, and development standards.
1. Project Overview & Current Status
Current Version: v0.2.18
License: Dual MIT / Apache 2.0
Repository Topology
| Remote | URL | Role | Status |
|---|---|---|---|
forgejo |
https://git.oiupoyt.space/oiupoyt/scythe.git |
Self-hosted primary; source of truth, release assets | Active |
spearzificial |
https://spearzificial@github.com/spearzificial/scythe.git |
Active development mirror (currently maintained account) | Active |
origin |
https://github.com/oiupoyt/scythe.git |
Legacy GitHub mirror | Read-only / stale token |
gitlab |
https://gitlab.com/oiupoyt.cat/scythe.git |
Optional mirror | Optional |
GitHub account policy: GitHub pushes now go to the spearzificial account. The oiupoyt GitHub OAuth token is expired, so origin is not a valid push target. Always name the account in the remote URL (https://spearzificial@github.com/...) so Git selects the correct stored credential when multiple GitHub accounts exist in the credential store.
Dual-Push Rule
Every push must reach both active remotes:
git push spearzificial main
git push forgejo main
Scythe is a cross-platform, ultra-low-overhead desktop screen recorder, instant replay buffer, and livestreamer written in Rust. It delivers functionality comparable to NVIDIA ShadowPlay and GPU Screen Recorder with minimal CPU and GPU overhead.
Core Capabilities:
- Unified Multicall Binary Architecture: UI overlay, CLI client, and headless daemon engine are consolidated into a single executable (
src/main.rs) with subcommands (scythe daemon,scythe record,scythe save, etc.) and backward-compatible symlinks (scythe-daemon,scythe-ui). - Zero-Copy Hardware Video Encoding:
- Linux: FFmpeg VA-API (
h264_vaapi,hevc_vaapi,av1_vaapi) with direct DRM DMA-BUF import for Intel and AMD GPUs. Automatic low-power hardware fallback handles drivers lackingVAEntrypointEncSliceLP. - Windows: Native NVENC, AMD AMF, Intel QuickSync (QSV), and multithreaded software fallbacks via DirectX Graphics Infrastructure (DXGI) desktop duplication.
- Linux: FFmpeg VA-API (
- High-Fidelity Audio Engine:
- Multi-source routing: System Audio, Microphone, Both, or Muted.
- Dual codec support: Native Opus (
AV_CODEC_ID_OPUS, 48 kHz stereo, 160 kbps,AV_SAMPLE_FMT_FLT) as recommended default, with native AAC (AV_CODEC_ID_AAC, 48 kHz stereo, 192 kbps,AV_SAMPLE_FMT_FLTP) as universal fallback. - Interactive sound verification suite with live dB / percentage VU meters and test tone playback.
- Lock-Free Circular RAM Replay Buffer:
- Continuous lock-free packet ring buffer in system RAM (configurable from 5 seconds to 30 minutes).
- Keyframe-aware draining ensures saved MP4 clips begin cleanly on IDR boundaries with zero decoder corruption.
- Smart Power Management:
- AC power connectivity monitoring via Linux
/sys/class/power_supplyand Windows Win32GetSystemPowerStatus. - Dedicated 1-second background power poller thread.
- "Power-only" replay mode automatically drains and pauses the replay buffer on battery power to conserve battery life, and seamlessly resumes with keyframe forced resync upon AC connection.
- AC power connectivity monitoring via Linux
- KAT Square Minimalist Interface:
- Strictly conforms to
DESIGN.mdguidelines:border-radius: 0 !important;across all windows, inputs, buttons, and badges. - Obsidian dark palette (
#0b0b0b/#111111) with hairline#222222framing and restrained functional accents. - Borderless, shadowless terminal-style notification toasts.
- Monospace typography option with integrated
Adwaita Monofont.
- Strictly conforms to
- Screenshot Resolution Presets:
- Intuitive presets:
Native (Source),1080p (FHD),1440p (QHD),2160p (4K UHD),720p (HD)with hardware-accelerated Lanczos scaling via FFmpeg.
- Intuitive presets:
- In-App Auto-Updater:
- Synchronizes against Forgejo releases API (
https://git.oiupoyt.space/api/v1/repos/oiupoyt/scythe/releases/latest) with automated binary replacement.
- Synchronizes against Forgejo releases API (
2. High-Level Architecture & Process Model
Scythe uses a unified multicall binary dispatch model:
+-----------------------------------+
| scythe binary |
+-----------------+-----------------+
|
Inspects argv[0] and first CLI argument
|
+---------------------------------+---------------------------------+
| |
v v
"scythe daemon" "scythe [command]"
or argv[0] == "scythe-daemon" or argv[0] == "scythe-ui"
| |
+--------v-------------------------+ +---------v-----------------------+
| Headless Daemon Engine | | Client CLI & Overlay |
| (src/daemon.rs) | <--- Unix Domain Socket / --- | (src/overlay_egui.rs) |
| - Screen Capture (PipeWire/DXGI) | Local TCP IPC Loop | - egui HUD Overlay (Alt+Z) |
| - Hardware Video Encoder | | - Audio Diagnostics & VU Meters |
| - Native Opus/AAC Audio Encoder | | - Clip Trimmer & Gallery |
| - Lock-Free Circular Ring Buffer | | - Terminal Notification Toasts |
| - AC Power Watcher Thread | | - Global Hotkey Listener Pump |
| - MP4 Muxer & RTMP Streamer | +---------------------------------+
+----------------------------------+
Multicall Dispatching:
- Daemon Mode: Invoked when
args[1] == "daemon",args[1] == "--mock", or when executed via symlinkscythe-daemon/vrec-daemon. Runs the headless capture and recording engine (src/daemon.rs). - Client / UI Mode: Invoked when executed as
scythe,scythe-ui, or with client arguments (save,record,screenshot,status,menu, etc.). Runssrc/main.rsand launchessrc/overlay_egui.rs.
3. Inter-Process Communication (IPC)
The protocol is implemented in src/ipc.rs.
Transport Layer:
- Linux: Unix Domain Socket at
$XDG_RUNTIME_DIR/scythe.sock(with legacy fallback to/run/user/<UID>/vrec.sock). - Windows: Local TCP socket on
127.0.0.1binding to candidate ports42069–42072and persisting the active port in%TEMP%\scythe-ipc.port.
Message Payload:
Packets are framed with a 4-byte little-endian length prefix followed by JSON-serialized structs:
- Command (
Command):SaveReplay: Drains the circular buffer and muxes an MP4 replay.StartRecording/StopRecording/ToggleRecording: Controls manual recording.StartStreaming/StopStreaming/ToggleStreaming: Controls RTMP broadcasting.ToggleAudio: Toggles microphone/system audio mute state.CycleAudioMode: Cycles through System -> Mic -> Both -> Muted.ToggleCursor: Inverts cursor visibility state and triggers capture synchronization.ShowOverlay: Opens the HUD overlay via the daemon if it is not already running (scythe --show-overlay). Distinct from--menu, which toggles an already-open overlay closed.ReloadConfig: Live-reloads configuration changes without restarting the daemon unless video parameters change.PowerStateChanged(bool): Internal notification dispatched by the power poller when AC state transitions.GetStatus: Requests the currentDaemonStatus.StopDaemon: Triggers clean daemon shutdown.
- DaemonStatus (
DaemonStatus):is_recording: boolrecording_duration_sec: u64is_replay_active: boolaudio_muted: boolaudio_mode: String ("system", "mic", "both", "muted")show_cursor: boolmic_volume/system_volume: f32mic_level_peak/system_level_peak: f32 (0.0 to 1.0 real-time peak values)is_streaming: boolstreaming_duration_sec: u64
4. Video Capture Subsystem
Capture sources implement the FrameSource trait located in src/capture/mod.rs.
Frame Representation:
pub enum Frame {
Raw {
width: u32,
height: u32,
stride: u32,
data: Arc<Vec<u8>>,
},
DmaBuf {
fd: std::os::fd::RawFd,
width: u32,
height: u32,
stride: u32,
format: u32,
modifier: u64,
offset: u32,
},
D3D11Texture {
handle: usize,
width: u32,
height: u32,
},
}
Platform Implementations:
- Wayland (
src/capture/wayland.rs&src/capture/wayland_stream.rs):- Uses
xdg-desktop-portaland PipeWire screencast stream. - Negotiates DMA-BUF memory buffers (
SPA_DATA_DmaBuf) for zero-copy GPU video encoding. - Falls back to shared memory buffers (
SPA_DATA_MemPtr) when DMA-BUF is unavailable.
- Uses
- X11 (
src/capture/x11.rs):- Uses MIT-SHM (
xcb::shm) for direct shared-memory frame transfer from the X server.
- Uses MIT-SHM (
- Windows (
src/capture/windows.rs):- Uses the DirectX Graphics Infrastructure (DXGI) Desktop Duplication API (
IDXGIOutputDuplication). - Iterates through all display adapters and outputs to probe and select the active desktop.
- Uses a single Direct3D 11 staging texture (
D3D11_USAGE_STAGING) withD3D11_CPU_ACCESS_READfor low-latency GPU readback. - Tightly packs rows into BGRA format if GPU pitch padding is detected.
- Ensures fallback / idle frames generate opaque black (
[0, 0, 0, 255]) to prevent uninitialized green chroma artifacts.
- Uses the DirectX Graphics Infrastructure (DXGI) Desktop Duplication API (
- Mock Capture (
src/capture/mock.rs):- Generates simulated 1080p frames for headless unit tests and CI runners where no display server is attached.
5. Video Encoding Pipeline
The video encoder module is located in src/encoder.rs.
Linux Encoder (VaapiEncoder):
- Uses FFmpeg libavcodec with VA-API hardware acceleration (
h264_vaapi,hevc_vaapi,av1_vaapi). - Direct DRM DMA-BUF Import: Zero-copy surface creation via
vaCreateSurfacesFromPropertiesfor Intel and AMD GPUs. - Color Standards: ITU-R BT.709 color primaries (
AVCOL_PRI_BT709), transfer characteristics (AVCOL_TRC_BT709), and MPEG limited range (AVCOL_RANGE_MPEG). - SIMD Color Space Conversion: When software fallback is required,
sws_scaleoperates withSWS_FAST_BILINEARand explicit ITU-709 coefficient mapping.
Windows Encoder (WindowsHwEncoder):
- Hardware Probing Priority:
- H.264:
h264_nvenc->h264_amf->h264_qsv->libx264 - HEVC:
hevc_nvenc->hevc_amf->hevc_qsv->libx265 - AV1:
av1_nvenc->av1_amf->av1_qsv->libsvtav1
- H.264:
- Standardized Pixel Format: NV12 (
AV_PIX_FMT_NV12) across hardware encoders, with YUV420P fallback. - Color Conversion: Converts captured BGRA to NV12 using
sws_scaleconfigured with ITU-R BT.709 matrix and full-range to limited-range mapping (sws_setColorspaceDetails), eliminating washed-out colors and green screen bugs. - Low Latency Settings: Rate control VBR with burst ceiling, zero B-frames (
max_b_frames = 0), ultrafast tuning, and 1-second GOP size (gop_size = fps).
6. Audio Subsystem
Audio capture and conversion is implemented in src/capture/audio.rs and src/encoder.rs.
Pipeline:
- Capture:
- Linux: Uses PulseAudio/PipeWire
parectargeting@DEFAULT_MONITOR@(for system audio loopback) and@DEFAULT_SOURCE@(for microphone input) with a 20ms low-latency buffer. - Windows: Direct WASAPI loopback capture using the
cpalcrate.
- Linux: Uses PulseAudio/PipeWire
- Channel Downmixing & Resampling:
- Downmixes 5.1 and 7.1 surround sound to stereo using center and surround coefficients:
L = FL + 0.707 * C + 0.707 * SL + 0.5 * LFE R = FR + 0.707 * C + 0.707 * SR + 0.5 * LFE - Resamples arbitrary input sample rates to 48,000 Hz using linear interpolation and soft-clipping limiting (
tanhcurve).
- Downmixes 5.1 and 7.1 surround sound to stereo using center and surround coefficients:
- Encoding (
AudioEncoder):- Opus (
AV_CODEC_ID_OPUS): Native Opus stereo at 160 kbps, 48 kHz with float sample format (AV_SAMPLE_FMT_FLT). Primary recommended standard. - AAC (
AV_CODEC_ID_AAC): Native AAC stereo at 192 kbps, 48 kHz with planar float format (AV_SAMPLE_FMT_FLTP). Universal fallback standard. - Microsecond-accurate PTS rebasing aligns audio packets against video IDR keyframes.
- Opus (
- Interactive Verification & Live VU Meters:
- The UI includes live decibel / percentage VU meters for both system sound and microphone.
- Integrated test suite (
test micandtest syswith automated chime playback) allows users to immediately verify sound capture without running external applications.
7. Lock-Free RAM Ring Buffer & Power Management
Implemented in src/ring.rs, src/daemon.rs, and src/config.rs.
Buffer Mechanics:
- Keeps encoded video and audio packets in RAM without writing to disk during active buffer mode.
- Fixed Capacity: Capacity is calculated dynamically from duration (seconds) * target framerate * safety factor. On low-power hardware, capacity is tightly bounded to conserve memory.
- Keyframe-Aware Pruning:
- Replay clips must always begin with an IDR keyframe to prevent visual corruption.
- When the circular buffer reaches maximum capacity, packets are dropped starting from the oldest non-keyframe sequence up to the next IDR keyframe boundary.
- Drain Operation: When
Command::SaveReplayis received, the buffer packets are cloned into an asynchronous channel without blocking the capture and encoding threads.
AC Power Management:
- Connectivity Detection:
ScytheConfig::is_ac_power_connected()inspects/sys/class/power_supply/*/onlineon Linux and callsGetSystemPowerStatuson Windows. - Poller Thread: A dedicated 1-second background thread continuously monitors AC power changes.
- Automatic Buffer Gating: When
autostart_power_onlyis enabled:- On battery power loss: The replay buffer is automatically paused, drained of existing frames, and a toast notification (
power lost, replay paused) is dispatched. - On AC connection restore: Replay buffering automatically resumes, forces an encoder keyframe, and dispatches a notification (
power restored, replay resumed).
- On battery power loss: The replay buffer is automatically paused, drained of existing frames, and a toast notification (
8. Muxer & Livestreaming
Implemented in src/muxer.rs.
MP4 Container Muxing (Muxer):
- Wraps FFmpeg libavformat to produce universal MP4 files (
.mp4) containing H.264/HEVC/AV1 video and Opus/AAC audio. - Emits faststart headers (
+faststart) to place themoovatom at the beginning of the file for instant streaming and playback. - Rebases PTS and DTS timestamps to zero for both video and audio streams at file initialization.
- Automatic Collision Handling: Formats filenames as
Replay-HH-MM-SS_DD-MM-YYYY.mp4orRecording-HH-MM-SS_DD-MM-YYYY.mp4and resolves conflicts via incremental suffixes (_1,_2).
RTMP Livestreaming (spawn_stream_worker):
- Connects to RTMP endpoints (Twitch, YouTube, Kick, Custom RTMP).
- Encapsulates video and audio NAL units into FLV/RTMP transport on a dedicated worker thread with non-blocking channel dispatch.
9. In-Game HUD Overlay & Notifications
Implemented in src/overlay_egui.rs.
Design System: KAT Square Minimalist:
- Strictly adheres to
DESIGN.md:border-radius: 0 !important;: Zero rounded corners or pill badges anywhere in the interface.- Obsidian Dark Palette:
#0b0b0broot background,#111111surface panels,#222222hairline borders. - Restrained Accents: Functional emerald (
#10b981), red (#ef4444), cyan (#38bdf8), and vendor themes (AMD, NVIDIA, Intel). - Monospace Typography: Full support for
Adwaita Mono/JetBrains Monowith an interface toggle.
Views:
- Main HUD:
- Rolldown / swipe animated dropdown menus (
ui.ctx().animate_bool(...)over 180ms) for Replay, Record, and Broadcast. - Quick actions with drop shadow framing.
- Rolldown / swipe animated dropdown menus (
- Settings View:
- File Information: Output directory with native folder pickers.
- Video Parameters: Resolution scaling, FPS (15 to 360), bitrate (5 to 150 Mbps), codec (H.264, HEVC, AV1), color range (Full vs Limited), low-power recording mode.
- Audio: Mode routing, volume sliders (0-150%), Opus/AAC codec choice, and live verification test suite.
- Screenshot: Resolution presets (
Native,1080p,1440p,2160p,720p), format (PNGvsJPEG), and cursor capture toggle. - General: Theme accents, 8 global keybinds with interactive recording listener, compact
Reset Hotkeysbutton, and typography toggle.
- Notification Toasts (
ShadowPlayToastApp):- Borderless, shadowless terminal-style card.
- Obsidian
#111111fill with 2px vertical accent rule and 6px status dot. - Uppercase monospace title, lowercase metadata subtitle, and 2px hairline progress countdown line.
10. Global Hotkeys & Window Hooks
Implemented in src/hotkey.rs and src/hyprland_binds.rs.
Keybind Registry:
- Menu Overlay:
Alt+Z - Save Instant Replay:
Ctrl+Shift+R - Start / Stop Recording:
Ctrl+Shift+F9 - Pause / Resume Recording:
Ctrl+Shift+F8 - Take Screenshot:
Ctrl+Shift+S - Toggle Mouse Cursor:
Ctrl+Shift+F10 - Mute / Unmute Microphone:
Ctrl+Shift+M - Mute / Unmute System Audio:
Ctrl+Shift+N
Dispatch Strategies:
- Hyprland Direct IPC: Registers binds dynamically using
hyprctl keyword bindto bypass Wayland global hotkey restrictions. Watcheshyprland.confmodifications and auto-reapplies. - Universal Desktop: Uses the
global-hotkeycrate for standard Windows and X11 hotkey registration.
11. Configuration Management
Implemented in src/config.rs.
File Locations:
- Linux:
~/.config/scythe/config.json - Windows:
%APPDATA%\scythe\config.json
Autostart Integration:
ScytheConfig::apply_autostart_system(enable_replay, enable_overlay) manages:
- Systemd user service:
systemctl --user enable/disable scythe-daemon.service. - Standard XDG autostart desktop entries in
~/.config/autostart/scythe-daemon.desktopandscythe-overlay.desktop.
Retired Settings (v0.2.18 cleanup):
These keys were accepted by the settings UI and persisted to disk, but nothing in the capture, encoder, or daemon paths ever read them. They were removed from ScytheConfig and the corresponding UI controls were deleted, so the overlay no longer presents options that do nothing:
| Retired key | Why it was removed |
|---|---|
capture_source |
Capture always selected the focused monitor; window/region modes were never implemented |
replay_storage |
Replay buffer is always RAM-backed; the disk path was never built |
change_resolution, resolution_w, resolution_h |
Encoder always used native capture dimensions |
container_format |
Muxer always writes MP4 |
show_scroll_lock_led |
No LED driver integration existed |
autostart_on_game |
No game-detection layer existed |
save_folder_by_game, name_by_game |
No game detection; filenames are timestamp-based |
language, ui_color_theme |
No translation layer or theme engine; accent_color drives the real palette |
record_enabled |
Dead field superseded by runtime is_recording state |
Unknown keys in an existing config.json are ignored by serde, so upgrading does not break saved configurations. scythe rewrites the file without these keys on the next save.
12. In-App Auto-Updater
Implemented in src/updater.rs.
Workflow:
- Queries the self-hosted Forgejo release API:
GET https://git.oiupoyt.space/api/v1/repos/oiupoyt/scythe/releases/latest(Falls back to GitHub API if Forgejo is unreachable). - Parses SemVer strings and checks if
is_newer(latest, CURRENT_VERSION). - Downloads the singular platform package:
- Linux:
scythe-v<version>-linux-x86_64.tar.gz - Windows:
scythe-v<version>-windows-x86_64.zip(includes standalone portable FFmpeg DLLs).
- Linux:
- Atomically swaps the executable in
~/.local/bin/scytheor%USERPROFILE%\.local\bin\scythe.exe.
13. Verification, Testing & Cross-Compilation
Local Linux Commands:
# Type check all targets
cargo check --all-targets
# Run the complete unit test suite (27 tests)
cargo test
# Strict clippy lint verification
cargo clippy --all-targets -- -D warnings
# Build optimized Linux release binary
cargo build --release
# Install release binary and symlinks to ~/.local/bin
bash install.sh
# Run end-to-end integration test suite
bash tests/e2e_test.sh
tests/e2e_test.sh refuses to unlink a socket owned by a running daemon. Unix sockets cannot be re-linked: removing the path entry of a live daemon leaves the kernel socket bound but unreachable, and the user's session stays broken until it restarts. The script therefore sends StopDaemon and waits for the socket to disappear first, aborting if the daemon will not stop.
Windows Cross-Compilation (from Linux Host):
FFMPEG_DIR="$HOME/.local/share/ffmpeg-win64/ffmpeg-n9.0-latest-win64-gpl-shared-9.0" \
PKG_CONFIG_PATH="$HOME/.local/share/ffmpeg-win64/ffmpeg-n9.0-latest-win64-gpl-shared-9.0/lib/pkgconfig" \
PKG_CONFIG_ALLOW_CROSS=1 \
cargo build --release --target x86_64-pc-windows-gnu
14. Behavioral Rules & Coding Standards
- ABSOLUTELY ZERO EMOJIS: Under no circumstances should emojis be added to code, comments, log outputs, commit messages, or communication.
- File & Symbol Links: Every file path or code symbol mentioned in communication must be formatted as a clickable markdown link using the
file://scheme (e.g.,src/encoder.rs). - KAT Square Minimalist: Strict
border-radius: 0 !important;on all visual elements. - Dual-Push Rule: When pushing commits or tags, always push to both active remotes (
forgejoandspearzificial):git push spearzificial <branch> git push forgejo <branch> - Memory Safety & Process Isolation: Avoid blocking the audio or video capture loops. Use unbounded or high-capacity crossbeam channels for frame dispatch.