windows-capture

repository·main·Indexed 17 days ago

https://github.com/niiightmarexd/windows-capture

A high-performance Rust and Python library for capturing screen content on Windows using the Graphics Capture API and DXGI Desktop Duplication API. Version 2.0.0 includes a hardware-accelerated video encoder with stable audio timing, support for capturing specific windows or monitors, and the ability to encode video to files or in-memory streams.

Tokens
18.3K
Snippets
58
Records
77
Agent score
65%

What's inside windows-capture

  1. How GraphicsCaptureApiHandler works

    main

    To capture screen content, you must implement the GraphicsCaptureApiHandler trait for a custom struct. This trait defines the lifecycle of a capture session through three main methods:

    1. new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error>: Called once to initialize your handler. The Context contains flags which are user-defined values (e.g., dimensions) passed from the Settings object.
    2. on_frame_arrived(&mut self, frame: &mut Frame, capture_control: InternalCaptureControl) -> Result<(), Self::Error>: Called every time a new frame is available. This is where you process frames (e.g., sending them to a VideoEncoder) or decide to stop the capture using capture_control.stop().
    3. on_closed(&mut self) -> Result<(), Self::Error>: An optional handler called when the capture item (like a window) is closed.

    You must also define associated types Flags (data passed to new) and Error (the error type returned by the handler).

    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> { /* ... */ }
        fn on_frame_arrived(&mut self, frame: &mut Frame, capture_control: InternalCaptureControl) -> Result<(), Self::Error> { /* ... */ }
        fn on_closed(&mut self) -> Result<(), Self::Error> { /* ... */ }
    }
  2. Use the Graphics Capture API with WindowsCapture

    main

    The WindowsCapture class provides an event-driven interface for capturing specific windows or monitors using the Windows Graphics Capture API. You can register callback functions using the @capture.event decorator to handle frame arrival and session closure.

    Key components:

    • WindowsCapture(...): Initializes the capture session. Parameters include cursor_capture, draw_border, monitor_index, and window_name.
    • on_frame_arrived(frame, capture_control): Triggered whenever a new frame is available. The frame object provides methods like save_as_image(), and capture_control allows you to call .stop() to end the session.
    • on_closed(): Triggered when the capture item (e.g., the target window) is closed. Returning from this function ends the session.
    from windows_capture import WindowsCapture, Frame, InternalCaptureControl
    
    # Initialize capture
    capture = WindowsCapture(
        cursor_capture=None,
        draw_border=None,
        monitor_index=None,
        window_name=None,
    )
    
    # Handle new frames
    @capture.event
    def on_frame_arrived(frame: Frame, capture_control: InternalCaptureControl):
        print("New frame arrived")
        frame.save_as_image("image.png")
        capture_control.stop()
    
    # Handle session closure
    @capture.event
    def on_closed():
        print("Capture session closed")
    
    capture.start()
  3. Use the DXGI Desktop Duplication API with DxgiDuplicationSession

    main

    For high-performance desktop duplication, use the DxgiDuplicationSession. This is suitable for capturing the entire desktop/monitor content.

    Workflow:

    1. Initialize DxgiDuplicationSession() (defaults to the primary monitor).
    2. Call session.acquire_frame(timeout_ms=...) to retrieve a frame. It returns None if no frame is available within the timeout.
    3. Process the frame using frame.to_numpy(copy=False) (returns a NumPy array with shape (height, width, 4)) or frame.save_as_image().
    4. Error Handling: If the session encounters access loss, it may raise a RuntimeError. In this case, call session.recreate() to restore the session.
    from windows_capture import DxgiDuplicationSession
    
    # Create a duplication session for the primary monitor
    session = DxgiDuplicationSession()
    
    # Grab a frame
    frame = session.acquire_frame(timeout_ms=33)
    if frame is not None:
        image = frame.to_numpy(copy=False)  # shape: (height, width, 4)
        frame.save_as_image("duplication.png")
    
    # Recreate the session if DXGI reports access loss
    try:
        session.acquire_frame()
    except RuntimeError:
        session.recreate()
  4. Use the Graphics Capture API

    main

    The Graphics Capture API is the primary way to capture specific windows or monitors using the latest Windows screen capture technology. You use the WindowsCapture class and register event handlers using the @capture.event decorator to process frames or handle session closure.

    Key components:

    • WindowsCapture: The main capture object. Configuration options include cursor_capture, draw_border, monitor_index, and window_name.
    • on_frame_arrived(frame, capture_control): An event triggered whenever a new frame is available. The frame object provides methods like save_as_image(), and capture_control (of type InternalCaptureControl) allows you to call .stop() to gracefully end the session.
    • on_closed(): An event triggered when the capture item closes (e.g., the target window is closed).
    from windows_capture import WindowsCapture, Frame, InternalCaptureControl
    
    # Initialize capture with optional configuration
    capture = WindowsCapture(
        cursor_capture=None,
        draw_border=None,
        monitor_index=None,
        window_name=None,
    )
    
    # Handle new frames
    @capture.event
    def on_frame_arrived(frame: Frame, capture_control: InternalCaptureControl):
        print("New frame arrived")
        # Save the frame as an image
        frame.save_as_image("image.png")
        # Stop the capture thread
        capture_control.stop()
    
    # Handle session closure
    @capture.event
    def on_closed():
        print("Capture session closed")
    
    capture.start()
  5. Use the DXGI Desktop Duplication API

    main

    The DxgiDuplicationSession provides an alternative high-performance pipeline for desktop duplication. This is useful for capturing the entire desktop content.

    Workflow:

    1. Initialize DxgiDuplicationSession().
    2. Call session.acquire_frame(timeout_ms=...) to retrieve a frame. It returns None if no frame is available within the timeout.
    3. Process the frame using frame.to_numpy(copy=False) (returns a NumPy array with shape (height, width, 4)) or frame.save_as_image().
    4. Error Handling: If the session encounters an access loss (e.g., due to display changes), it may raise a RuntimeError. In this case, call session.recreate() to restore the session.
    from windows_capture import DxgiDuplicationSession
    
    # Create a duplication session for the primary monitor
    session = DxgiDuplicationSession()
    
    # Grab a frame (returns None if no frame is available within the timeout)
    frame = session.acquire_frame(timeout_ms=33)
    if frame is not None:
        image = frame.to_numpy(copy=False)  # shape: (height, width, 4)
        frame.save_as_image("duplication.png")
    
    # Recreate the session if DXGI reports access loss
    try:
        session.acquire_frame()
    except RuntimeError:
        session.recreate()
  6. Install the windows-capture Rust library

    main

    To use windows-capture in your Rust project, add it to your Cargo.toml dependencies or use the cargo add command.

    Using Cargo.toml:

    [dependencies]
    windows-capture = "2.0.0"

    Using CLI:

    cargo add windows-capture
    cargo add windows-capture
  7. Implement the GraphicsCaptureApiHandler trait to handle capture events

    main

    To capture screen content, you must implement the GraphicsCaptureApiHandler trait. This trait defines how your application responds to new frames and capture lifecycle events.

    Key methods to implement:

    • new(ctx: Context<Self::Flags>): Initializes your handler. The ctx.flags allows you to pass custom data (like dimensions) from your Settings to your handler.
    • on_frame_arrived(&mut self, frame: &mut Frame, capture_control: InternalCaptureControl): Called whenever a new frame is available. You can use the frame to encode video, save images, or access raw buffer data. Use capture_control.stop() to end the session.
    • on_closed(&mut self): (Optional) Called when the capture item (e.g., a window) is closed.

    You must also define two associated types:

    • type Flags: The type of data passed from Settings to new via Context.
    • type Error: The error type returned by the handler methods.
    use windows_capture::capture::{Context, GraphicsCaptureApiHandler};
    use windows_capture::frame::Frame;
    use windows_capture::graphics_capture_api::InternalCaptureControl;
    
    struct MyCapture;
    
    impl GraphicsCaptureApiHandler for MyCapture {
        type Flags = (i32, i32);
        type Error = Box<dyn std::error::Error + Send + Sync>;
    
        fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
            // ctx.flags contains the data passed from Settings
            Ok(MyCapture)
        }
    
        fn on_frame_arrived(
            &mut self, 
            frame: &mut Frame, 
            capture_control: InternalCaptureControl
        ) -> Result<(), Self::Error> {
            // Process frame (e.g., encode or save)
            Ok(())
        }
    }
  8. Query and manage windows with the `Window` struct

    main

    The Window struct provides utilities for finding, inspecting, and managing top-level Windows windows. You can use it to retrieve window titles, process names, dimensions, and monitor information, or to check if a window is suitable for capture.

    Common tasks include:

    • Getting the foreground window via Window::foreground().
    • Finding a window by its exact title via Window::from_name(title).
    • Finding a window by a substring in its title via Window::from_contains_name(substring).
    • Enumerating all capturable windows via Window::enumerate().
    • Converting a Window into a GraphicsCaptureItemType for use in capture sessions.
    use windows_capture::window::Window;
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let window = Window::foreground()?;
        println!("Foreground window title: {}", window.title()?);
    
        Ok(())
    }
  9. Understand GraphicsCaptureItemType

    main

    The GraphicsCaptureItemType enum wraps a WinRT GraphicsCaptureItem with additional context about the source being captured. This allows the API to distinguish between different types of capture targets:

    • Monitor((GraphicsCaptureItem, Monitor)): A display monitor, including its Monitor details.
    • Window((GraphicsCaptureItem, Window)): An application window, including its Window details.
    • Unknown((GraphicsCaptureItem, HwndGuard)): An unknown source, typically created from an HWND, including an HwndGuard.
    pub enum GraphicsCaptureItemType {
        Monitor((GraphicsCaptureItem, Monitor)),
        Window((GraphicsCaptureItem, Window)),
        Unknown((GraphicsCaptureItem, HwndGuard)),
    }
  10. Capture the screen with WindowsCapture

    main

    The WindowsCapture class is the primary interface for screen capture. You can capture a specific monitor by monitor_index or a specific window using window_name (substring match) or window_hwnd (Window Handle).

    To use it, you must provide callback functions for when a frame arrives and when the capture session closes using the @event decorator. You can start the capture in the main thread using .start() or in a dedicated thread using .start_free_threaded().

    from windows_capture import WindowsCapture
    
    @WindowsCapture.event
    def on_frame_arrived(frame, control):
        # Process the frame
        frame.save_as_image("capture.png")
        # To stop the capture from within the callback:
        # control.stop()
    
    @WindowsCapture.event
    def on_closed():
        print("Capture closed")
    
    capture = WindowsCapture(window_name="Notepad")
    capture.start()
  11. Use DxgiDuplicationApi to capture a monitor

    main

    The DxgiDuplicationApi is a wrapper around the Windows DXGI Desktop Duplication API. It allows you to capture a specific monitor by providing a Monitor object. You can acquire frames from the duplication session, which can then be mapped to CPU-readable buffers for processing or saving as images.

    To use it, create a new session with DxgiDuplicationApi::new(monitor), then call acquire_next_frame(timeout_ms) to get a DxgiDuplicationFrame. From the frame, you can extract a DxgiDuplicationFrameBuffer using .buffer().

    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>> {
        // Select the primary monitor
        let monitor = Monitor::primary()?;
    
        // Create a duplication session for this monitor
        let mut dup = DxgiDuplicationApi::new(monitor)?;
    
        // Try to grab one frame within ~33ms (about 30 FPS budget)
        let mut frame = dup.acquire_next_frame(33)?;
    
        // Map the GPU image into CPU memory and save a PNG
        let mut buffer = frame.buffer()?;
        buffer.save_as_image("dup.png", ImageFormat::Png)?;
        Ok(())
    }