Retrieving a buffer from windows_capture

⚓ Rust    📅 2025-06-27    👤 surdeus    👁️ 7      

surdeus

Warning

This post was published 43 days ago. The information described in this article may have changed.
use std::{
    io::{self, Write},
    sync::{Arc, Mutex},
};

use windows_capture::{
    capture::{Context, GraphicsCaptureApiHandler},
    frame::Frame,
    graphics_capture_api::InternalCaptureControl,
    settings::{ColorFormat, CursorCaptureSettings, DrawBorderSettings, Settings},
    window::Window,
};

pub struct Capture {
    buf: Arc<Mutex<Vec<u8>>>,
    once: bool,
}

pub struct Flags {
    pub buf: Arc<Mutex<Vec<u8>>>,
    pub once: bool,
}

impl GraphicsCaptureApiHandler for Capture {
    type Flags = Flags;

    type Error = Box<dyn std::error::Error + Send + Sync>;

    fn new(ctx: Context<Self::Flags>) -> Result<Self, Self::Error> {
        let flags = ctx.flags;
        Ok(Capture {
            buf: flags.buf,
            once: flags.once,
        })
    }

    fn on_frame_arrived(
        &mut self,
        frame: &mut Frame,
        capture_control: InternalCaptureControl,
    ) -> Result<(), Self::Error> {
        io::stdout().flush()?;
        let mut frame_buf = frame.buffer().unwrap();
        let buf = frame_buf.as_raw_buffer();

        let mut curr_buf = self.buf.lock().unwrap();
        curr_buf.clear();
        curr_buf.write(buf).unwrap();

        if self.once {
            capture_control.stop();
        }

        Ok(())
    }
}

pub fn capture(buf: Arc<Mutex<Vec<u8>>>) -> Result<(), CaptureError> {
    let window = Window::foreground();

    let Ok(window) = window else {
        return Err(CaptureError::NotFound);
    };

    let settings = Settings::new(
        window,
        CursorCaptureSettings::Default,
        DrawBorderSettings::WithoutBorder,
        ColorFormat::Rgba8,
        Flags {
            buf: buf.clone(),
            once: false,
        },
    );

    Capture::start_free_threaded(settings).map_err(|_| CaptureError::NotFound)?;
    Ok(())
}

#[cfg(test)]
pub fn capture_once(buf: Arc<Mutex<Vec<u8>>>) -> Result<(), CaptureError> {
    let window = Window::foreground();

    let Ok(window) = window else {
        return Err(CaptureError::NotFound);
    };

    let settings = Settings::new(
        window,
        CursorCaptureSettings::Default,
        DrawBorderSettings::WithoutBorder,
        ColorFormat::Rgba8,
        Flags {
            buf: buf.clone(),
            once: true,
        },
    );

    Capture::start(settings).map_err(|_| CaptureError::NotFound)?;
    Ok(())
}

#[derive(Debug)]
pub enum CaptureError {
    NotFound,
}

This code is working.
The capture function is entry point in there. It is called when i need to receive current window image.

The problem is i have to pass a buffer as flag and it feels like a hack to me. I guess flags should change behavior and not the functionality.

Is there any better way to write that?

As far as i can see i cannot return image just like i want. There is some function on handle that will return the struct and that's how i can do that, but the problem with this is that if i need the image again i will have to recapture the window.

And i need image at least every 250ms so i definitely want to avoid recapturing

1 post - 1 participant

Read full topic

🏷️ rust_feed