Documentation · Version 1.0.0
Embedding VeloFrame
Everything you need to bring screenshots and clips into your app.
Overview
VeloFrame was written specifically for ClipHub, the clipping app by LiGLP Studios, and is open source: github.com/LiGLP-Studios/VeloFrame. Business use, or use in apps that are not open source, requires a license from LiGLP Studios.
VeloFrame has no user interface. The host application (e.g. ClipHub) decides when to capture, by hotkey, button or automation, and what happens with the result. VeloFrame does the capturing.
- Engine only. Hotkeys, settings and display belong to the host application. VeloFrame receives finished requests and returns results with measurements.
- Two ways to embed. As a Rust library (
veloframe::Engine) or as a separate process (veloframe.exe, JSON lines over stdin/stdout). The process route protects the host from crashes and works with any language. - Stay warm.
warm_up()opens capture up front and buffers are reused, so even the first screenshot is fast. - Everything is measured. Every answer contains per-step timings and which source delivered the image.
Not yet: audio in clips, mouse cursor in video, Windows Graphics Capture (phase 2), and macOS and Linux. The Mac and Linux versions are being worked on.
Build, test, measure
VeloFrame is a Rust workspace. The release build ends up in target/release/veloframe.exe.
cargo build --release
cargo test --workspace
# Benchmarks
cargo run --release -p veloframe-bench -- encode "Screenshot.png" # compare encoders
cargo run --release -p veloframe-bench -- capture 20 # measure real screenshots
cargo run --release -p veloframe-bench -- memory # memory after each step
cargo run --release -p veloframe-bench -- clip performance 30 # measure and verify the clip buffer
cargo run --release -p veloframe-bench -- clip long 60
As a Rust library
The direct route for Rust apps. warm_up() opens capture up front so even the first screenshot is fast.
use veloframe::{Engine, ScreenshotRequest, ImageFormat, Area, Rect};
let mut engine = Engine::new()?;
engine.warm_up()?; // open capture up front
let shot = engine.screenshot(&ScreenshotRequest {
format: ImageFormat::Jpeg,
quality: 90,
cursor: false,
area: Area::Window, // foreground window, otherwise the screen
screen: Rect::new(0, 0, 2560, 1440), // physical pixels
folder: "C:\\Users\\Name\\Pictures\\Screenshots".into(),
clipboard: true, // copied to the clipboard while encoding
})?;
println!("{} in {:.1} ms", shot.path, shot.total_ms);
As a separate process
For Electron, Node, Python, C# and everything else: start veloframe.exe and send one JSON message per line on stdin. Answers arrive line by line on stdout. If the engine crashes, the host application keeps running.
→ {"id":1,"cmd":"screenshot","args":{"format":"png","area":"screen",
"screen":{"x":0,"y":0,"width":1920,"height":1080},"folder":"C:\\Pictures"}}
← {"id":1,"ok":true,"result":{"path":"C:\\Pictures\\Screenshot 2026-11-05 163141.png",
"width":1920,"height":1080,"captureMs":4.0,"encodeMs":25.0,"totalMs":29.0,"warm":true}}
- Start the process. VeloFrame announces itself with a
readyevent and pre-warms capture on all screens. - Send requests with a unique
id, one per line. - Match answers by
id.oktells you whether it worked.
The protocol is compatible with the previous ClipHub capture helper. ClipHub can start veloframe.exe instead of cliphub-capture.exe without changing its Electron code.
Commands
| Command | Arguments | Result |
|---|---|---|
ping | – | "pong" |
version | – | Engine, version, active capture source |
system-check | – | GPUs, monitors, HDR, encoders, available capture paths |
warm-up | – | Open capture on all screens up front (veloframe.exe does this on start) |
screenshot | format (png/jpeg), quality, cursor, area (screen/window), screen, folder, clipboard | Path, size, bytes, source, per-step timings |
copy | path | Copy the last screenshot to the clipboard again, otherwise error not-cached |
buffer-start | mode (performance/long), seconds, fps, quality (low…ultra), resolution (native, 1440p, 1080p, 720p), screen, optional codec (h264/hevc), storage (file/ram) | Plan, encoder, accepted settings |
buffer-status | – | Seconds and bytes in the buffer, encoded and dropped frames (by reason), encoder effort |
save-clip | folder, optional seconds | Path, size, duration, frames, codec, save time |
buffer-stop | – | Stop the buffer, release memory and encoder |
Timings in every answer
Screenshot answers contain captureMs, encodeMs, writeMs, totalMs and clipboardMs, so you can always see where time is lost.
Errors
Every error has a fixed code for your app and a message you can show your users directly. React to code in your code, not to the text.
{"id":1,"ok":false,"error":"Message for the user","code":"no-screen"}
| Code | Meaning |
|---|---|
invalid-request | Request incomplete or malformed |
no-screen | No matching screen found |
capture-lost | Capture lost (resolution change, UAC, fullscreen switch). VeloFrame reopens once and tries again |
capture-failed | Capture failed |
save-failed | File could not be saved |
clipboard-failed | Clipboard not available |
not-cached | No screenshot in memory for copy |
unsupported | Not possible on this system |
Screenshots
The flow of a screenshot request, from the call to the file:
Request (Rust API or JSON line)
└─ ScreenshotPipeline::take core/pipeline.rs
├─ Target: screen or foreground window
├─ ScreenSource::capture -> RgbImage Win/duplication.rs
│ └─ screen with the largest share of the target, D3D device on its GPU,
│ BGRA -> RGB (4 pixels per step), rotation handled
├─ draw mouse cursor (optional) Win/desktop.rs
├─ in parallel: clipboard (own thread) Win/clipboard.rs
│ encode PNG/JPEG in memory core/codec (all cores)
│ then write in one go core/screenshot.rs
└─ result with measurements; image stays in memory for `copy`
Custom encoders
Both split the image into horizontal strips, one per CPU core, and join the parts into an ordinary file.
- PNG: All five PNG filters are computed per row (with AVX2 when the CPU supports it), the best one wins. Then a custom DEFLATE encoder with its own Huffman code per strip. Verified lossless.
- JPEG: Each strip is encoded separately (SIMD) and joined with restart markers. The file decodes pixel for pixel exactly like a single pass.
- The game comes first: The helper threads run below normal priority and only use CPU time that is currently free.
Clip engine
The clip buffer continuously captures a screen, encodes in hardware and keeps the last seconds ready. On request they are saved as MP4, without re-encoding.
Modes
| Performance | Long | |
|---|---|---|
| Length | 5–60 s | 5–300 s |
| Codec | HEVC (if the GPU supports it), otherwise H.264 | H.264 (plays everywhere) |
| Bitrate 1080p60 “high” | mean 6.2 / cap 10.4 Mbit/s, follows the content | mean 13.6 / cap 16 Mbit/s |
| Keyframe | every 2 s | every second |
| Encoder effort | 85, lowered when needed | 70, lowered when needed |
| GPU priority | behind the game | normal |
| Buffer | temporary file | temporary file |
Codec and buffer location can be overridden per request (codec, storage).
Per-frame flow (60 fps = every 16.7 ms)
Desktop Duplication ─► GPU texture (only when something changed)
│
GPU video processor: BGRA → NV12, BT.709, scaling ─► frame from the encoder pool
│
Queue (up to 4 frames) ─► hardware encoder (NVENC / AMF / QuickSync)
│
Packets ─► replay buffer (fixed ring, always a keyframe at the front)
│
save-clip ─► MP4 (index at the front, packets unchanged)
What VeloFrame does itself
- Queue in front of the encoder: Short delays no longer cost a frame. Dropped frames under load: from 8–11 % down to 0.4–1.6 %.
- Encoder effort controller: If more than 1 % of frames are lost, VeloFrame lowers the effort while running and raises it again once there is headroom. Smooth video comes before the last percent of compression.
- Buffer in a temporary file: Windows keeps it in the cache where possible and can give the memory back to the game at any time. It is deleted on exit, even after a crash.
- Saving without a copy: The index is copied under the lock, the data is read afterwards while recording continues.
- One capture for both: While the buffer runs, screenshots of that screen come from the live image (17 ms total).
- Clean teardown: The encoder is drained before shutdown. 40 out of 40 runs end cleanly.
Known limitations
- No audio and no mouse cursor in the video yet.
- One screen per buffer; rotated screens not yet supported.
- A clip starts at the last keyframe before the requested start (Performance: up to 2 s earlier). Frame-accurate start is still to do.
- Windows 10 only plays HEVC with the “HEVC Video Extensions” from the Microsoft Store. Browsers (Chrome, Edge, Electron) play HEVC with hardware support. For maximum compatibility use
codec: "h264". - The NVIDIA HEVC encoder does not accept B-frames.
Architecture
The pipeline lives platform-independently in core. The platform crates only implement two interfaces:
ScreenSourcedelivers an image of a desktop region (Desktop Duplication, later Windows Graphics Capture, ScreenCaptureKit, PipeWire).Desktopdelivers foreground window, mouse cursor, clipboard and timestamp.
| Folder | Crate | Contents |
|---|---|---|
core | veloframe-core | Image types, interfaces, screenshot pipeline, custom encoders, clip modes, replay buffer, MP4 writer, protocol, errors |
engine | veloframe | The engine API (Engine) and the veloframe.exe program |
Win | veloframe-win | Desktop Duplication, mouse cursor, clipboard, system check, clip buffer (GPU capture, NV12, hardware encoder) |
Mac | veloframe-mac | ScreenCaptureKit, VideoToolbox (in progress) |
Linux | veloframe-linux | PipeWire, xdg-desktop-portal, X11, VAAPI (in progress) |
bench | veloframe-bench | Benchmark tool: compare encoders, measure screenshots, memory and the clip buffer |
Benchmarks
Release build, Intel Core i5-6400 (4 cores), RTX 3050, Windows 10, two monitors on two GPUs. Measured under load: other programs were already using 60–70 % of the CPU.
Screenshot engine memory
| State | Task Manager | Total working set | Committed |
|---|---|---|---|
| After start + warm-up | 23.5 MB | 45.5 MB | 57.5 MB |
| After screenshots | 29.9 MB | 52.1 MB | 64.1 MB |
| After a screenshot with clipboard | 37.9 MB | 60.1 MB | 72.0 MB |
- Task Manager shows the private working set: memory used only by VeloFrame.
- Total working set additionally includes the GPU driver files (about 22 MB), shared by all programs on the same GPU, including the game.
- CPU when idle: 0.00 %, no background polling. 20 more screenshots change usage by less than 1 MB, so no memory leak.
Platforms
| Platform | Status | Technology |
|---|---|---|
| Windows 10 / 11 | Available | Desktop Duplication, Media Foundation, D3D11 |
| macOS | In progress | ScreenCaptureKit, VideoToolbox |
| Linux | In progress | PipeWire, xdg-desktop-portal, X11, VAAPI |
The Mac and Linux versions are still being worked on. The interfaces are the same as on Windows, so your integration won't need to change for them later.
Windows roadmap
| Phase | Contents | Status |
|---|---|---|
| 0 · Foundation | Types, IPC protocol compatible with ClipHub | Done |
| 1 · Desktop Duplication | Everything the ClipHub helper can do: monitors, rotation, DPI, active window, PNG/JPEG, clipboard | Done |
| Intermediate · Speed | Pre-warming, custom encoders on all cores, benchmark tool | Done |
| 2 · Windows Graphics Capture | Capture single windows, even when something lies on top | Planned |
| 3 · GPU processing | Crop and HDR → SDR via shaders | Planned |
| 4 · Output | WebP and metadata (time, game name, monitor) | Partly |
| 5 · Automatic selection | Best capture path with fallback, no screenshot fails without reason | Planned |
| 6 · Benchmark & polish | Measure FPS loss in the game, compare with the old helper | Planned |
Targets
| Metric | Target |
|---|---|
| Key press to finished file, 1440p, JPEG | under 50 ms |
| Key press to finished file, 1440p, PNG | under 150 ms |
| FPS loss in the game | not measurable (under 1 %) |
| RAM when idle | under 30 MB |
| CPU when idle | close to 0 % |
Open source and license
VeloFrame is open source. The complete source code is on GitHub: github.com/LiGLP-Studios/VeloFrame.
| Use | What you need |
|---|---|
| Open-source projects | Free to use, no license from LiGLP Studios needed |
| Business use | License from LiGLP Studios |
| Apps that are not open source | License from LiGLP Studios |
Request a license: for business use or closed-source apps, write to business@liglp.de or get in touch via liglp.de.