Windows Capture β

π Windows Capture 2.0.0 is here! π
- π¬ Enhanced video encoder: hardware-accelerated with improved stability and monotonic audio timing
- π₯οΈ New support for the DXGI Desktop Duplication API
Windows Capture is a highly efficient Rust and Python library that enables you to capture the screen using the Graphics Capture API effortlessly. This library allows you to easily capture the screen of your Windows-based computer and use it for various purposes, such as creating instructional videos, taking screenshots, or recording your gameplay. With its intuitive interface and robust functionality, Windows Capture is an excellent choice for anyone looking for a reliable, easy-to-use screen-capturing solution.
Note: This README is for the Rust library. The Python library can be found here: https://github.com/NiiightmareXD/windows-capture/tree/main/windows-capture-python
Recall.ai - API for desktop recording
If youβre looking for a hosted desktop recording API, consider checking out Recall.ai, an API that records Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.
Features
- Updates frames only when required
- High performance
- Easy to use
- Uses the latest Windows Graphics Capture API
- Supports the DXGI Desktop Duplication API
- Enhanced, hardware-accelerated video encoder with stable audio timing
Installation
Add this dependency to your Cargo.toml:
[dependencies]
windows-capture = "2.0.0"
Or run this command:
cargo add windows-capture
Usage
use std::io::{self, Write};
use std::time::Instant;
use windows_capture::capture::{Context, GraphicsCaptureApiHandler};
use windows_capture::encoder::{
AudioSettingsBuilder, ContainerSettingsBuilder, VideoEncoder, VideoSettingsBuilder,
};
use windows_capture::frame::Frame;
use windows_capture::graphics_capture_api::InternalCaptureControl;
use windows_capture::graphics_capture_picker::GraphicsCapturePicker;
use windows_capture::settings::{
ColorFormat, CursorCaptureSettings, DirtyRegionSettings, DrawBorderSettings,
MinimumUpdateIntervalSettings, SecondaryWindowSettings, Settings,
};
struct Capture {
encoder: Option<VideoEncoder>,
start: Instant,
}
impl GraphicsCaptureApiHandler for Capture {
type Flags = (i32, i32);
type Error = Box<dyn std::error::Error + Send + Sync>;
fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
let encoder = VideoEncoder::new(
VideoSettingsBuilder::new(ctx.flags.0 as u32, ctx.flags.1 as u32),
AudioSettingsBuilder::default().disabled(true),
ContainerSettingsBuilder::default(),
"video.mp4",
)?;
Ok(Self { encoder: Some(encoder), start: Instant::now() })
}
fn on_frame_arrived(
&mut self,
frame: &mut Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Self::Error> {
print!("\rRecording for: {} seconds", self.start.elapsed().as_secs());
io::stdout().flush()?;
self.encoder.as_mut().unwrap().send_frame(frame)?;
if self.start.elapsed().as_secs() >= 6 {
self.encoder.take().unwrap().finish()?;
capture_control.stop();
println!();
}
Ok(())
}
fn on_closed(&mut self) -> Result<(), Self::Error> {
println!("Capture session ended");
Ok(())
}
}
fn main() {
let item = GraphicsCapturePicker::pick_item().expect("Failed to pick item");
let Some(item) = item else {
println!("No item selected");
return;
};
let size = item.size().expect("Failed to get item size");
let settings = Settings::new(
item,
CursorCaptureSettings::Default,
DrawBorderSettings::Default,
SecondaryWindowSettings::Default,
MinimumUpdateIntervalSettings::Default,
DirtyRegionSettings::Default,
ColorFormat::Rgba8,
size,
);
Capture::start(settings).expect("Screen capture failed");
}
Stream-based encoding example
Instead of writing directly to a file, you can encode into an in-memory stream using VideoEncoder::new_from_stream. This is useful when you need to send the encoded video over a network, pipe it to another process, or process it further before saving.
use std::io::{self, Write};
use std::time::Instant;
use windows::Storage::Streams::InMemoryRandomAccessStream;
use windows::core::Interface;
use windows_capture::capture::{Context, GraphicsCaptureApiHandler};
use windows_capture::encoder::{
AudioSettingsBuilder, ContainerSettingsBuilder, VideoEncoder, VideoSettingsBuilder,
};
use windows_capture::frame::Frame;
use windows_capture::graphics_capture_api::InternalCaptureControl;
use windows_capture::graphics_capture_picker::GraphicsCapturePicker;
use windows_capture::settings::{
ColorFormat, CursorCaptureSettings, DirtyRegionSettings, DrawBorderSettings,
MinimumUpdateIntervalSettings, SecondaryWindowSettings, Settings,
};
struct StreamCapture {
encoder: Option<VideoEncoder>,
stream: InMemoryRandomAccessStream,
start: Instant,
}
impl GraphicsCaptureApiHandler for StreamCapture {
type Flags = (i32, i32);
type Error = Box<dyn std::error::Error + Send + Sync>;
fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
let stream = InMemoryRandomAccessStream::new()?;
let encoder = VideoEncoder::new_from_stream(
VideoSettingsBuilder::new(ctx.flags.0 as u32, ctx.flags.1 as u32),
AudioSettingsBuilder::default().disabled(true),
ContainerSettingsBuilder::default(),
stream.cast()?,
)?;
Ok(Self {
encoder: Some(encoder),
stream,
start: Instant::now(),
})
}
fn on_frame_arrived(
&mut self,
frame: &mut Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Self::Error> {
print!(
"\rStreaming for: {} seconds | Buffer size: {} bytes",
self.start.elapsed().as_secs(),
self.stream.Size()?
);
io::stdout().flush()?;
self.encoder.as_mut().unwrap().send_frame(frame)?;
if self.start.elapsed().as_secs() >= 6 {
self.encoder.take().unwrap().finish()?;
let size = self.stream.Size()?;
println!("\nCapture finished. Stream contains {size} bytes.");
let reader = windows::Storage::Streams::DataReader::CreateDataReader(
&self.stream.GetInputStreamAt(0)?,
)?;
reader.LoadAsync(size as u32)?.join()?;
let mut bytes = vec![0u8; size as usize];
reader.ReadBytes(&mut bytes)?;
std::fs::write("stream_output.mp4", &bytes)?;
println!("Saved stream to stream_output.mp4");
capture_control.stop();
}
Ok(())
}
fn on_closed(&mut self) -> Result<(), Self::Error> {
println!("Capture session ended");
Ok(())
}
}
fn main() {
let item = GraphicsCapturePicker::pick_item().expect("Failed to pick item");
let Some(item) = item else {
println!("No item selected");
return;
};
let size = item.size().expect("Failed to get item size");
let settings = Settings::new(
item,
CursorCaptureSettings::Default,
DrawBorderSettings::Default,
SecondaryWindowSettings::Default,
MinimumUpdateIntervalSettings::Default,
DirtyRegionSettings::Default,
ColorFormat::Rgba8,
size,
);
StreamCapture::start(settings).expect("Stream capture failed");
}
DXGI Desktop Duplication example
use windows_capture::dxgi_duplication_api::DxgiDuplicationApi;
use windows_capture::encoder::ImageFormat;
use windows_capture::monitor::Monitor;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let monitor = Monitor::primary()?;
let mut dup = DxgiDuplicationApi::new(monitor)?;
let mut frame = dup.acquire_next_frame(33)?;
frame.save_as_image("dxgi_screenshot.png", ImageFormat::Png)?;
Ok(())
}
Documentation
Detailed documentation for each API and type is available at https://docs.rs/windows-capture.
Contributing
Contributions are welcome! If you find a bug or want to add a feature, please open an issue or submit a pull request.
License
This project is licensed under the MIT License.