From 2ebce7d0f58392e4c2148f2789821b994031f391 Mon Sep 17 00:00:00 2001 From: NiiightmareXD Date: Thu, 9 Nov 2023 06:50:28 -0800 Subject: [PATCH] =?UTF-8?q?Added=20Capture=20Control=20=F0=9F=8E=89=20=09m?= =?UTF-8?q?odified:=20=20=20Cargo.lock=20=09modified:=20=20=20Cargo.toml?= =?UTF-8?q?=20=09modified:=20=20=20Python/WindowsCapture/main.py=20=09modi?= =?UTF-8?q?fied:=20=20=20Python/windows-capture-native/Cargo.toml=20=09mod?= =?UTF-8?q?ified:=20=20=20Python/windows-capture-native/src/lib.rs=20=09de?= =?UTF-8?q?leted:=20=20=20=20src/buffer.rs=20=09modified:=20=20=20src/capt?= =?UTF-8?q?ure.rs=20=09modified:=20=20=20src/d3d11.rs=20=09modified:=20=20?= =?UTF-8?q?=20src/frame.rs=20=09modified:=20=20=20src/graphics=5Fcapture?= =?UTF-8?q?=5Fapi.rs=20=09modified:=20=20=20src/lib.rs=20=09modified:=20?= =?UTF-8?q?=20=20src/monitor.rs=20=09modified:=20=20=20src/settings.rs=20?= =?UTF-8?q?=09modified:=20=20=20src/window.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 41 +++++- Cargo.toml | 2 +- Python/WindowsCapture/main.py | 28 ++-- Python/windows-capture-native/Cargo.toml | 4 + Python/windows-capture-native/src/lib.rs | 59 +++++--- src/buffer.rs | 22 --- src/capture.rs | 113 ++++++++++++++- src/d3d11.rs | 8 +- src/frame.rs | 170 ++++++++++++++--------- src/graphics_capture_api.rs | 60 +++----- src/lib.rs | 1 - src/monitor.rs | 2 +- src/settings.rs | 2 +- src/window.rs | 2 +- 14 files changed, 344 insertions(+), 170 deletions(-) delete mode 100644 src/buffer.rs diff --git a/Cargo.lock b/Cargo.lock index 0572327..fd01ae0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,16 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -254,6 +264,28 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ndarray" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -414,6 +446,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.8.0" @@ -552,8 +590,8 @@ version = "1.0.22" dependencies = [ "image", "log", + "ndarray", "parking_lot", - "rayon", "thiserror", "windows", ] @@ -563,6 +601,7 @@ name = "windows-capture-native" version = "1.0.22" dependencies = [ "pyo3", + "windows", "windows-capture", ] diff --git a/Cargo.toml b/Cargo.toml index fb3b8ba..e27e263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ resolver = "2" [dependencies] image = "0.24.7" log = "0.4.20" +ndarray = "0.15.6" parking_lot = "0.12.1" -rayon = "1.8.0" thiserror = "1.0.49" windows = { version = "0.51.1", features = [ "Win32_System_WinRT_Graphics_Capture", diff --git a/Python/WindowsCapture/main.py b/Python/WindowsCapture/main.py index f286a9f..4cc9910 100644 --- a/Python/WindowsCapture/main.py +++ b/Python/WindowsCapture/main.py @@ -1,7 +1,6 @@ -# Python Version Is NOT Complete Yet - - from windows_capture_native import NativeWindowsCapture +import ctypes +import numpy class Capture: @@ -9,7 +8,7 @@ def __init__(self, capture_cursor: bool = True, draw_border: bool = False): self.frame_handler = None self.closed_handler = None self.capture = NativeWindowsCapture( - False, False, self.on_frame_arrived, self.on_closed + True, False, self.on_frame_arrived, self.on_closed ) self.capture_cursor = capture_cursor self.draw_border = draw_border @@ -17,9 +16,21 @@ def __init__(self, capture_cursor: bool = True, draw_border: bool = False): def start(self): self.capture.start() - def on_frame_arrived(self, frame): + def stop(self): + self.capture.stop() + + def on_frame_arrived(self, buffer_ptr, width, height, row_pitch): if self.frame_handler: - self.frame_handler(frame) + num_array = numpy.ctypeslib.as_array( + ctypes.cast(buffer_ptr, ctypes.POINTER(ctypes.c_uint8)), + shape=(height, row_pitch), + ) + + if row_pitch == width * 4: + self.frame_handler(num_array.reshape(height, width, 4)) + else: + self.frame_handler(num_array[:, : width * 4].reshape(height, width, 4)) + else: raise Exception("on_frame_arrived Event Handler Is Not Set") @@ -43,8 +54,9 @@ def event(self, handler): @capture.event -def on_frame_arrived(frame): - print("on_frame_arrived") +def on_frame_arrived(frame_bytes): + print("lol") + capture.stop() @capture.event diff --git a/Python/windows-capture-native/Cargo.toml b/Python/windows-capture-native/Cargo.toml index 48e8a07..d6fd5a8 100644 --- a/Python/windows-capture-native/Cargo.toml +++ b/Python/windows-capture-native/Cargo.toml @@ -24,4 +24,8 @@ crate-type = ["cdylib"] [dependencies] pyo3 = "0.20.0" +windows = { version = "0.51.1", features = [ + "Win32_UI_WindowsAndMessaging", + "Win32_Foundation", +] } windows-capture = { path = "../../" } diff --git a/Python/windows-capture-native/src/lib.rs b/Python/windows-capture-native/src/lib.rs index 5b8b3c1..b0ac75e 100644 --- a/Python/windows-capture-native/src/lib.rs +++ b/Python/windows-capture-native/src/lib.rs @@ -7,12 +7,16 @@ #![warn(clippy::cargo)] #![allow(clippy::redundant_pub_crate)] -use std::{sync::Arc, time::Instant}; - -use pyo3::{ - prelude::*, - types::{PyBytes, PyTuple}, +use std::{ + sync::{ + atomic::{self, AtomicBool}, + Arc, + }, + time::Instant, }; + +use pyo3::prelude::*; +use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage; use windows_capture::{ capture::WindowsCaptureHandler, frame::Frame, monitor::Monitor, settings::WindowsCaptureSettings, @@ -25,16 +29,19 @@ fn windows_capture_native(_py: Python, m: &PyModule) -> PyResult<()> { Ok(()) } +/// Internal Struct Used For Windows Capture #[pyclass] pub struct NativeWindowsCapture { capture_cursor: bool, draw_border: bool, on_frame_arrived_callback: Arc, on_closed: Arc, + active: Arc, } #[pymethods] impl NativeWindowsCapture { + /// Create A New Windows Capture Struct #[new] #[must_use] pub fn new( @@ -48,9 +55,11 @@ impl NativeWindowsCapture { draw_border, on_frame_arrived_callback: Arc::new(on_frame_arrived_callback), on_closed: Arc::new(on_closed), + active: Arc::new(AtomicBool::new(true)), } } + /// Start Capture pub fn start(&mut self) { let settings = WindowsCaptureSettings::new( Monitor::primary(), @@ -59,45 +68,59 @@ impl NativeWindowsCapture { ( self.on_frame_arrived_callback.clone(), self.on_closed.clone(), + self.active.clone(), ), ) .unwrap(); InnerNativeWindowsCapture::start(settings).unwrap(); } + + /// Stop Capture + pub fn stop(&mut self) { + println!("STOP"); + self.active.store(false, atomic::Ordering::Relaxed); + } } -pub struct InnerNativeWindowsCapture { +/// Internal Capture Struct Used From NativeWindowsCapture +struct InnerNativeWindowsCapture { on_frame_arrived_callback: Arc, on_closed: Arc, + active: Arc, } impl WindowsCaptureHandler for InnerNativeWindowsCapture { - type Flags = (Arc, Arc); + type Flags = (Arc, Arc, Arc); - fn new((on_frame_arrived_callback, on_closed): Self::Flags) -> Self { + fn new((on_frame_arrived_callback, on_closed, active): Self::Flags) -> Self { Self { on_frame_arrived_callback, on_closed, + active, } } fn on_frame_arrived(&mut self, mut frame: Frame) { + if !self.active.load(atomic::Ordering::Relaxed) { + unsafe { PostQuitMessage(0) }; + return; + } + let instant = Instant::now(); + let width = frame.width(); + let height = frame.height(); let buf = frame.buffer().unwrap(); - - let buf_bytes: &[u8] = unsafe { - std::slice::from_raw_parts(buf.as_ptr().cast::(), std::mem::size_of_val(buf)) - }; + let buf = buf.as_raw_buffer(); + let row_pitch = buf.len() / height as usize; Python::with_gil(|py| { - let buf_pybytes = PyBytes::new(py, buf_bytes); + py.check_signals().unwrap(); - let args = PyTuple::new(py, [buf_pybytes]); - - self.on_frame_arrived_callback.call1(py, args) - }) - .unwrap(); + self.on_frame_arrived_callback + .call1(py, (buf.as_ptr() as isize, width, height, row_pitch)) + .unwrap(); + }); println!("Took: {}", instant.elapsed().as_nanos() as f32 / 1000000.0); } diff --git a/src/buffer.rs b/src/buffer.rs deleted file mode 100644 index 4e4576f..0000000 --- a/src/buffer.rs +++ /dev/null @@ -1,22 +0,0 @@ -/// To Send Raw Pointers Between Threads -pub struct SendSyncPtr(pub *mut T); - -impl SendSyncPtr { - pub const fn new(ptr: *mut T) -> Self { - Self(ptr) - } -} - -unsafe impl Send for SendSyncPtr {} -unsafe impl Sync for SendSyncPtr {} - -/// To Send Raw Pointers Between Threads -pub struct SendPtr(pub *mut T); - -impl SendPtr { - pub const fn new(ptr: *mut T) -> Self { - Self(ptr) - } -} - -unsafe impl Send for SendPtr {} diff --git a/src/capture.rs b/src/capture.rs index 5286056..e470bdb 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1,9 +1,17 @@ -use log::{info, trace}; +use std::{ + os::windows::prelude::AsRawHandle, + thread::{self, JoinHandle}, +}; + +use log::{info, trace, warn}; +use thiserror::Error; use windows::{ Foundation::AsyncActionCompletedHandler, Win32::{ + Foundation::{HANDLE, LPARAM, WPARAM}, System::{ Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED, COINIT_SPEED_OVER_MEMORY}, + Threading::GetThreadId, WinRT::{ CreateDispatcherQueueController, DispatcherQueueOptions, DQTAT_COM_NONE, DQTYPE_THREAD_CURRENT, @@ -12,7 +20,8 @@ use windows::{ UI::{ HiDpi::{SetProcessDpiAwareness, PROCESS_PER_MONITOR_DPI_AWARE}, WindowsAndMessaging::{ - DispatchMessageW, GetMessageW, PostQuitMessage, TranslateMessage, MSG, + DispatchMessageW, GetMessageW, PostQuitMessage, PostThreadMessageW, + TranslateMessage, MSG, WM_QUIT, }, }, }, @@ -22,6 +31,81 @@ use crate::{ frame::Frame, graphics_capture_api::GraphicsCaptureApi, settings::WindowsCaptureSettings, }; +/// Used To Handle Capture Control Errors +#[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] +pub enum CaptureControlError { + #[error("Failed To Join Thread")] + FailedToJoin, +} + +/// Struct Used To Control Capture Thread +pub struct CaptureControl { + thread_handle: Option>>>, +} + +impl CaptureControl { + /// Create A New Capture Control Struct + #[must_use] + pub fn new( + thread_handle: JoinHandle>>, + ) -> Self { + Self { + thread_handle: Some(thread_handle), + } + } + + /// Wait Until The Thread Stops + pub fn wait(mut self) -> Result<(), Box> { + if let Some(thread_handle) = self.thread_handle.take() { + match thread_handle.join() { + Ok(result) => result?, + Err(_) => { + return Err(Box::new(CaptureControlError::FailedToJoin)); + } + } + } + + Ok(()) + } + + /// Gracefully Stop The Capture Thread + pub fn stop(mut self) -> Result<(), Box> { + if let Some(thread_handle) = self.thread_handle.take() { + let handle = thread_handle.as_raw_handle(); + let handle = HANDLE(handle as isize); + let therad_id = unsafe { GetThreadId(handle) }; + + loop { + match unsafe { + PostThreadMessageW(therad_id, WM_QUIT, WPARAM::default(), LPARAM::default()) + } { + Ok(_) => break, + Err(e) => { + if thread_handle.is_finished() { + break; + } + + if e.code().0 == -2147023452 { + warn!("Thread Is Not In Message Loop Yet"); + } else { + Err(e)?; + } + } + } + } + + match thread_handle.join() { + Ok(result) => result?, + Err(_) => { + return Err(Box::new(CaptureControlError::FailedToJoin)); + } + } + } + + Ok(()) + } +} + /// Event Handler Trait pub trait WindowsCaptureHandler: Sized { type Flags; @@ -29,9 +113,10 @@ pub trait WindowsCaptureHandler: Sized { /// Starts The Capture And Takes Control Of The Current Thread fn start( settings: WindowsCaptureSettings, - ) -> Result<(), Box> + ) -> Result<(), Box> where - Self: std::marker::Send + 'static, + Self: Send + 'static, + ::Flags: Send, { // Initialize COM trace!("Initializing COM"); @@ -53,8 +138,13 @@ pub trait WindowsCaptureHandler: Sized { // Start Capture info!("Starting Capture Thread"); let trigger = Self::new(settings.flags); - let mut capture = GraphicsCaptureApi::new(settings.item, trigger)?; - capture.start_capture(settings.capture_cursor, settings.draw_border)?; + let mut capture = GraphicsCaptureApi::new( + settings.item, + trigger, + settings.capture_cursor, + settings.draw_border, + )?; + capture.start_capture()?; // Message Loop trace!("Entering Message Loop"); @@ -97,6 +187,17 @@ pub trait WindowsCaptureHandler: Sized { Ok(()) } + /// Starts The Capture Without Taking Control Of The Current Thread + fn start_free_threaded(settings: WindowsCaptureSettings) -> CaptureControl + where + Self: Send + 'static, + ::Flags: Send, + { + let thread_handle = thread::spawn(move || Self::start(settings)); + + CaptureControl::new(thread_handle) + } + /// Function That Will Be Called To Create The Struct The Flags Can Be /// Passed From Settings fn new(flags: Self::Flags) -> Self; diff --git a/src/d3d11.rs b/src/d3d11.rs index 1164258..98d87b1 100644 --- a/src/d3d11.rs +++ b/src/d3d11.rs @@ -31,7 +31,7 @@ impl SendDirectX { #[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for SendDirectX {} -/// Used To Handle Internal DirectX Errors +/// Used To Handle DirectX Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum DirectXErrors { #[error("Failed To Create DirectX Device With The Recommended Feature Level")] @@ -39,8 +39,8 @@ pub enum DirectXErrors { } /// Create ID3D11Device And ID3D11DeviceContext -pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Box> -{ +pub fn create_d3d_device() +-> Result<(ID3D11Device, ID3D11DeviceContext), Box> { // Set Feature Flags let feature_flags = [ D3D_FEATURE_LEVEL_11_1, @@ -80,7 +80,7 @@ pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Box Result> { +) -> Result> { let dxgi_device: IDXGIDevice = d3d_device.cast()?; let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device)? }; let device: IDirect3DDevice = inspectable.cast()?; diff --git a/src/frame.rs b/src/frame.rs index 9b161ee..3038c73 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,34 +1,21 @@ -use std::{mem, ptr}; +use std::path::Path; -use image::ColorType; -use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use image::{Rgba, RgbaImage}; +use ndarray::{s, ArrayBase, ArrayView, Dim, OwnedRepr}; use thiserror::Error; use windows::Win32::Graphics::Direct3D11::{ ID3D11DeviceContext, ID3D11Texture2D, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ, }; -use crate::buffer::SendSyncPtr; - -/// Used To Handle Internal Frame Errors +/// Used To Handle Frame Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum FrameError { #[error("Graphics Capture API Is Not Supported")] InvalidSize, } -/// Pixels Color Representation -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -#[repr(C)] -pub struct Rgba { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, -} - -/// Frame Struct Used To Crop And Get The Frame Buffer +/// Frame Struct Used To Get The Frame Buffer pub struct Frame<'a> { - buffer: *mut u8, texture: ID3D11Texture2D, frame_surface: ID3D11Texture2D, context: &'a ID3D11DeviceContext, @@ -40,7 +27,6 @@ impl<'a> Frame<'a> { /// Craete A New Frame #[must_use] pub const fn new( - buffer: *mut u8, texture: ID3D11Texture2D, frame_surface: ID3D11Texture2D, context: &'a ID3D11DeviceContext, @@ -48,7 +34,6 @@ impl<'a> Frame<'a> { height: u32, ) -> Self { Self { - buffer, texture, frame_surface, context, @@ -70,7 +55,7 @@ impl<'a> Frame<'a> { } /// Get The Frame Buffer - pub fn buffer(&mut self) -> Result<&[Rgba], Box> { + pub fn buffer(&mut self) -> Result> { // Copy The Real Texture To Copy Texture unsafe { self.context @@ -89,59 +74,114 @@ impl<'a> Frame<'a> { )?; }; - // Create A Slice From The Bits - let slice = if self.width * 4 == mapped_resource.RowPitch { - // Means There Is No Padding And We Can Do Our Work - unsafe { - std::slice::from_raw_parts( - mapped_resource.pData as *const Rgba, - (self.height * mapped_resource.RowPitch) as usize / std::mem::size_of::(), - ) - } - } else { - // There Is Padding So We Have To Work According To: - // https://learn.microsoft.com/en-us/windows/win32/medfound/image-stride - - let row_size = self.width as usize * std::mem::size_of::(); - let send_sync_ptr = SendSyncPtr::new(self.buffer); - let send_sync_pdata = SendSyncPtr::new(mapped_resource.pData.cast::()); - - (0..self.height).into_par_iter().for_each(|i| { - let send_sync_ptr = &send_sync_ptr; - let send_sync_pdata = &send_sync_pdata; - - unsafe { - ptr::copy_nonoverlapping( - send_sync_pdata - .0 - .add((i * mapped_resource.RowPitch) as usize), - send_sync_ptr.0.add(i as usize * row_size), - row_size, - ); - }; - }); - - unsafe { - std::slice::from_raw_parts( - self.buffer.cast::(), - (self.width * self.height) as usize, - ) - } + // Get The Mapped Resource Data Slice + let mapped_frame_data = unsafe { + std::slice::from_raw_parts( + mapped_resource.pData as *const u8, + (self.height * mapped_resource.RowPitch) as usize, + ) }; - Ok(slice) + // Create Frame Buffer From Slice + let frame_buffer = FrameBuffer::new(mapped_frame_data, self.width, self.height); + + Ok(frame_buffer) } /// Save The Frame As An Image To Specified Path - pub fn save_as_image(&mut self, path: &str) -> Result<(), Box> { + pub fn save_as_image>( + &mut self, + path: T, + ) -> Result<(), Box> { let buffer = self.buffer()?; - let buf = unsafe { - std::slice::from_raw_parts(buffer.as_ptr().cast::(), mem::size_of_val(buffer)) - }; + let nopadding_buffer = buffer.as_raw_nopadding_buffer()?; // ArrayView<'a, u8, Dim<[usize; 3]> + + let (height, width, _) = nopadding_buffer.dim(); + + let mut rgba_image: RgbaImage = RgbaImage::new(width as u32, height as u32); + + for y in 0..height { + for x in 0..width { + let r = nopadding_buffer[(y, x, 0)]; + let g = nopadding_buffer[(y, x, 1)]; + let b = nopadding_buffer[(y, x, 2)]; + let a = nopadding_buffer[(y, x, 3)]; + + rgba_image.put_pixel(x as u32, y as u32, Rgba([r, g, b, a])); + } + } - image::save_buffer(path, buf, self.width, self.height, ColorType::Rgba8)?; + rgba_image.save(path)?; Ok(()) } } + +/// Frame Buffer Struct Used To Get Raw Pixel Data +pub struct FrameBuffer<'a> { + raw_buffer: &'a [u8], + width: u32, + height: u32, +} + +impl<'a> FrameBuffer<'a> { + /// Create A New Frame Buffer + #[must_use] + pub const fn new(raw_buffer: &'a [u8], width: u32, height: u32) -> Self { + Self { + raw_buffer, + width, + height, + } + } + + /// Get The Frame Buffer Width + #[must_use] + pub const fn width(&self) -> u32 { + self.width + } + + /// Get The Frame Buffer Height + #[must_use] + pub const fn height(&self) -> u32 { + self.height + } + + /// Get The Frame Buffer Height + #[must_use] + pub const fn has_padding(&self) -> bool { + let raw_buffer = self.as_raw_buffer(); + + self.width as usize * 4 != raw_buffer.len() / self.height as usize + } + + /// Get The Raw Pixel Data Might Have Padding + #[must_use] + pub const fn as_raw_buffer(&self) -> &'a [u8] { + self.raw_buffer + } + + /// Get The Raw Pixel Data Without Padding + #[allow(clippy::type_complexity)] + pub fn as_raw_nopadding_buffer( + &self, + ) -> Result, Dim<[usize; 3]>>, Box> { + let row_pitch = self.raw_buffer.len() / self.height as usize; + + let array = + ArrayView::from_shape((self.height as usize, row_pitch), self.raw_buffer)?.to_owned(); + + if self.width as usize * 4 == self.raw_buffer.len() / self.height as usize { + let array = array.into_shape((self.height as usize, self.width as usize, 4))?; + + Ok(array) + } else { + let array = array + .slice_move(s![.., ..self.width as usize * 4]) + .into_shape((self.height as usize, self.width as usize, 4))?; + + Ok(array) + } + } +} diff --git a/src/graphics_capture_api.rs b/src/graphics_capture_api.rs index 5980360..a0b2398 100644 --- a/src/graphics_capture_api.rs +++ b/src/graphics_capture_api.rs @@ -1,9 +1,6 @@ -use std::{ - alloc::{self, Layout}, - sync::{ - atomic::{self, AtomicBool}, - Arc, - }, +use std::sync::{ + atomic::{self, AtomicBool}, + Arc, }; use log::{info, trace}; @@ -30,36 +27,35 @@ use windows::{ }; use crate::{ - buffer::SendPtr, capture::WindowsCaptureHandler, d3d11::{create_d3d_device, create_direct3d_device, SendDirectX}, frame::Frame, }; -/// Used To Handle Internal Capture Errors +/// Used To Handle Capture Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum WindowsCaptureError { #[error("Graphics Capture API Is Not Supported")] Unsupported, - #[error("Graphics Capture API Changing Cursor Status Is Not Supported")] + #[error("Graphics Capture API Toggling Cursor Capture Is Not Supported")] CursorUnsupported, - #[error("Graphics Capture API Changing Border Status Is Not Supported")] + #[error("Graphics Capture API Toggling Border Capture Is Not Supported")] BorderUnsupported, #[error("Already Started")] AlreadyStarted, } -/// Struct To Use For Graphics Capture Api +/// Struct Used For Graphics Capture Api pub struct GraphicsCaptureApi { _item: GraphicsCaptureItem, _d3d_device: ID3D11Device, _direct3d_device: IDirect3DDevice, _d3d_device_context: ID3D11DeviceContext, - buffer_layout: Layout, - buffer_ptr: *mut u8, frame_pool: Option>, session: Option, active: bool, + capture_cursor: Option, + draw_border: Option, } impl GraphicsCaptureApi { @@ -67,7 +63,9 @@ impl GraphicsCaptureApi { pub fn new( item: GraphicsCaptureItem, callback: T, - ) -> Result> { + capture_cursor: Option, + draw_border: Option, + ) -> Result> { // Check Support if !ApiInformation::IsApiContractPresentByMajor( &HSTRING::from("Windows.Foundation.UniversalApiContract"), @@ -76,15 +74,6 @@ impl GraphicsCaptureApi { return Err(Box::new(WindowsCaptureError::Unsupported)); } - // Allocate 256MB Of Memory (This Makes It So There Is No Need To Ever - // Reallocate And It Supports Up To 16K Monitor Resolution) - trace!("Allocating 256MB Of Memory"); - let buffer_layout = Layout::new::<[u8; 256 * 1024 * 1024]>(); - let buffer_ptr = unsafe { alloc::alloc(buffer_layout) }; - if buffer_ptr.is_null() { - alloc::handle_alloc_error(buffer_layout); - } - // Create DirectX Devices trace!("Creating DirectX Devices"); let (d3d_device, d3d_device_context) = create_d3d_device()?; @@ -141,7 +130,6 @@ impl GraphicsCaptureApi { let mut last_size = item.Size()?; let callback_frame_arrived = callback; let direct3d_device_recreate = SendDirectX::new(direct3d_device.clone()); - let buffer_frame_arrived = SendPtr::new(buffer_ptr); move |frame, _| { // Return Early If The Capture Is Closed @@ -226,9 +214,7 @@ impl GraphicsCaptureApi { }; let texture = texture.unwrap(); - let buffer_frame_arrived = &buffer_frame_arrived; let frame = Frame::new( - buffer_frame_arrived.0, texture, frame_surface, &context, @@ -255,27 +241,23 @@ impl GraphicsCaptureApi { _d3d_device: d3d_device, _direct3d_device: direct3d_device, _d3d_device_context: d3d_device_context, - buffer_layout, - buffer_ptr, frame_pool: Some(frame_pool), session: Some(session), active: false, + capture_cursor, + draw_border, }) } /// Start Capture - pub fn start_capture( - &mut self, - capture_cursor: Option, - draw_border: Option, - ) -> Result<(), Box> { + pub fn start_capture(&mut self) -> Result<(), Box> { // Check If The Capture Is Already Installed if self.active { return Err(Box::new(WindowsCaptureError::AlreadyStarted)); } // Config - if capture_cursor.is_some() { + if self.capture_cursor.is_some() { if ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsCursorCaptureEnabled"), @@ -283,13 +265,13 @@ impl GraphicsCaptureApi { self.session .as_ref() .unwrap() - .SetIsCursorCaptureEnabled(capture_cursor.unwrap())?; + .SetIsCursorCaptureEnabled(self.capture_cursor.unwrap())?; } else { return Err(Box::new(WindowsCaptureError::CursorUnsupported)); } } - if draw_border.is_some() { + if self.draw_border.is_some() { if ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsBorderRequired"), @@ -297,7 +279,7 @@ impl GraphicsCaptureApi { self.session .as_ref() .unwrap() - .SetIsBorderRequired(draw_border.unwrap())?; + .SetIsBorderRequired(self.draw_border.unwrap())?; } else { return Err(Box::new(WindowsCaptureError::BorderUnsupported)); } @@ -357,9 +339,5 @@ impl Drop for GraphicsCaptureApi { if let Some(session) = self.session.take() { session.Close().expect("Failed to Close Capture Session"); } - - // Deallocate 256MB Of Memory - trace!("Deallocating 256MB Of Memory"); - unsafe { alloc::dealloc(self.buffer_ptr, self.buffer_layout) }; } } diff --git a/src/lib.rs b/src/lib.rs index 2123a32..f480f33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,6 @@ #![warn(clippy::nursery)] #![warn(clippy::cargo)] -mod buffer; pub mod capture; mod d3d11; pub mod frame; diff --git a/src/monitor.rs b/src/monitor.rs index 33655ff..8e8e6aa 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -10,7 +10,7 @@ use windows::{ }, }; -/// Used To Handle Internal Monitor Errors +/// Used To Handle Monitor Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum MonitorErrors { #[error("Failed To Find Monitor")] diff --git a/src/settings.rs b/src/settings.rs index a6ad6c6..168f07c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,7 +1,7 @@ use thiserror::Error; use windows::Graphics::Capture::GraphicsCaptureItem; -/// Used To Handle Internal Settings Errors +/// Used To Handle Settings Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum SettingsErrors { #[error("Failed To Convert To GraphicsCaptureItem")] diff --git a/src/window.rs b/src/window.rs index 989acd3..bb1e7a4 100644 --- a/src/window.rs +++ b/src/window.rs @@ -16,7 +16,7 @@ use windows::{ }, }; -/// Used To Handle Internal Window Errors +/// Used To Handle Window Errors #[derive(Error, Eq, PartialEq, Clone, Copy, Debug)] pub enum WindowErrors { #[error("Failed To Get The Foreground Window")]