Cross-platform screen and audio capture for Rust: raw frames, real metadata, native backends.
Getting started • Platform support • Troubleshooting • API docs • Type reference (any platform)
pinray is for Rust developers building screen recorders, streaming and remote-desktop tools, or apps and agents that need to see the screen. It talks to each operating system's native capture API and gives you raw video and audio frames with their metadata intact: stride, pixel format, timestamps, sequence numbers, and drop signals. It doesn't encode. You pipe frames into ffmpeg, WebRTC, wgpu, an image crate, or your own pipeline.
- Native backends, no wrapper crates: XDG Desktop Portal + PipeWire and X11 on Linux, ScreenCaptureKit (via objc2) on macOS, and DXGI Desktop Duplication, Windows Graphics Capture, and WASAPI (via the
windowscrate) on Windows - Audio built in: system-mix capture on every platform, timestamped on the same clock as video, with no
cpaldetour or shelling out topactl - Complete frame metadata: every frame carries width, height, stride, pixel format, a monotonic timestamp, and a per-stream sequence number, and dropped frames show up as explicit
Gapevents - Backend selection you can inspect:
Autopicks the right backend per platform, with fallbacks such as DXGI to WGC, andsession.backend_info()tells you which one you got and its caveats - Documented platform differences: X11 polls, DXGI delivers on change, and Wayland goes through a permission portal. Platform support lists every gap
| Video | System audio | Notes | |
|---|---|---|---|
| Linux Wayland | ✅ portal + PipeWire streaming | ✅ PipeWire | restore tokens skip the dialog |
| Linux X11 | ✅ paced polling (GetImage) | ✅ PipeWire | XFixes cursor blend |
| macOS 13.0+ | ✅ ScreenCaptureKit | ✅ ScreenCaptureKit | display + window capture |
| Windows 10+ | ✅ DXGI + WGC | ✅ WASAPI loopback | DXGI→WGC auto-fallback |
Per-backend limits, such as window capture and crop support, are in docs/platforms.md.
cargo add pinrayThis captures the primary display plus everything the system plays, and prints each event:
use std::time::Duration;
use pinray::{
AudioCapture, CaptureEvent, CaptureSession, SourceId, VideoCaptureTarget,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut session = CaptureSession::builder()
.video_target(VideoCaptureTarget::Display(SourceId::new("auto")))
.audio(AudioCapture::SystemMix)
.build()?;
println!("backend: {:?}", session.backend_info().kind);
session.start()?;
loop {
match session.next_event(Some(Duration::from_secs(5)))? {
CaptureEvent::Video(f) => {
println!("video {}x{} t={}ns", f.width, f.height, f.stream_time_ns)
}
CaptureEvent::Audio(f) => {
println!("audio {} Hz {}ch", f.sample_rate, f.channels)
}
CaptureEvent::Gap(g) => println!("gap: {:?}", g.reason),
CaptureEvent::End => break,
}
}
session.stop()?;
Ok(())
}Getting started covers picking a display or window with pinray::enumerate_sources(), audio-only capture (no permission dialog on Linux and Windows), forcing a backend with BackendPreference, and writing frames to a video file.
| Platform | Build | Runtime |
|---|---|---|
| Linux | libpipewire-0.3-dev + clang |
PipeWire; XDG portal for Wayland video |
| macOS | none | macOS 13.0+, Screen Recording permission |
| Windows | none | Windows 10+ (WGC needs 1903+) |
Where to look for what:
- docs.rs/pinray: API reference for every public item, with examples for the main entry points, plus the getting-started guide
- docs.rs/pinray-core: the same types, built for the default target, and the backend traits if you write your own backend
- Getting started: install, first frames, sources, audio, tuning, and muxing
- Platform support: the feature matrix and per-backend behavior
- Troubleshooting: build errors and capture problems by platform
- llms.txt: the whole API surface on one page, for coding agents
- CHANGELOG: what changed in each release
cargo run --example wayland_smoke # Linux Wayland: video + audio
cargo run --example x11_smoke # Linux X11: polling video + audio
cargo run --example audio_smoke # Linux/Windows: audio only
cargo run --example macos_smoke # macOS
cargo run --example windows_smoke # Windows (PINRAY_BACKEND=wgc|dxgi to force)pinray is a Cargo workspace. pinray-core defines the frame model and backend traits. Each platform crate owns its native and unsafe code: platform-linux, platform-macos, and platform-windows. pinray is the public facade that picks the platform backend. A future encoder crate can be added without touching capture.
pinray is at 0.2 and changing fast. Minor versions may break the API while the frame model and builder settle, so pin a minor version if you need stability.
pinray needs Rust 1.88 or later, for let-chains. Raising the minimum counts as a minor-version change while pinray is at 0.x.
Issues and pull requests are welcome. CONTRIBUTING.md covers setup, the checks CI runs, and how to test on each platform. When you report a bug, include the session.backend_info() output and a RUST_LOG=debug log, because backend selection differs per machine.
To report a security problem, see SECURITY.md instead of opening an issue.
MIT. See LICENSE. Contact: rupamgolui@proton.me
