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 a capture infrastructure crate: it talks to each OS's native capture API and hands you raw video and audio frames with their metadata intact - stride, pixel format, timestamps, sequence numbers, drop signals. Encoding is deliberately out of scope; pipe frames into ffmpeg, WebRTC, wgpu, image crates, or your own pipeline.
- Native backends, no wrapper crates - XDG Desktop Portal + PipeWire and X11 on Linux, ScreenCaptureKit (via objc2) on macOS, DXGI Desktop Duplication + Windows Graphics Capture + WASAPI (via the
windowscrate) on Windows. - Audio is first-class - system-mix capture on all four backends, timestamped on the same clock as video, no
cpaldetour and no shelling out topactl. - A frame model that doesn't lie - every frame carries width/height/stride, pixel format, color-space hint, a monotonic timestamp, and a per-stream sequence number. Dropped frames surface as explicit
Gapevents. - Inspectable backend selection -
Autopicks the right backend per platform (with fallbacks like DXGI→WGC), andsession.backend_info()tells you exactly what you got and its caveats. - Honest about platform differences - X11 is documented as polling, DXGI as change-driven, Wayland as portal-mediated. No pretending all platforms behave the same.
| 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 12.3+ | ✅ ScreenCaptureKit | ✅ ScreenCaptureKit | display + window capture |
| Windows 10+ | ✅ DXGI + WGC | ✅ WASAPI loopback | DXGI→WGC auto-fallback |
Full matrix with per-backend limitations: docs/platforms.md.
cargo add pinrayuse std::time::Duration;
use pinray::{AudioCapture, CaptureEvent, CaptureSession, SourceId, VideoCaptureTarget};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Capture the primary display plus everything the system plays.
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(())
}Pick specific displays/windows with pinray::enumerate_sources(), capture audio-only without any permission dialogs, force a backend with BackendPreference - see Getting started.
| Platform | Build | Runtime |
|---|---|---|
| Linux | libpipewire-0.3-dev + clang |
PipeWire; XDG portal for Wayland video |
| macOS | - | macOS 12.3+, Screen Recording permission |
| Windows | - | Windows 10+ (WGC needs 1903+) |
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)Cargo workspace: pinray-core defines the frame model and backend traits, one crate per platform owns its native/unsafe code (platform-linux, platform-macos, platform-windows), and pinray is the public facade. A future encoder crate can slot in without touching capture.
pinray is 0.2 and moving fast: minor versions may break the API while the frame model and builder settle against real-world use. Pin a minor version if you need stability.
Rust 1.88 (let-chains). MSRV bumps are minor-version changes during 0.2.
Issues and PRs welcome. Please include session.backend_info() output and RUST_LOG=debug logs in bug reports - backend selection differs per machine. CI runs clippy (warnings deny), tests on Linux/macOS/Windows, cross-target checks, and a real Xvfb capture smoke test.
MIT - see LICENSE. Contact: rupamgolui@proton.me
