From 4d6be030ba2b0cea565ccaee28b1999f25b8dd5d Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 7 Nov 2024 04:08:56 +0800 Subject: [PATCH 1/3] raw pixel buffers --- src/capturer/engine/mac/mod.rs | 129 +++++++++------- src/capturer/engine/mac/pixel_buffer.rs | 192 ++++++++++++++++++++++++ src/capturer/engine/mac/pixelformat.rs | 36 ++--- src/capturer/engine/mod.rs | 48 ++++-- src/capturer/mod.rs | 28 +++- 5 files changed, 342 insertions(+), 91 deletions(-) create mode 100644 src/capturer/engine/mac/pixel_buffer.rs diff --git a/src/capturer/engine/mac/mod.rs b/src/capturer/engine/mac/mod.rs index e036d56..4649239 100644 --- a/src/capturer/engine/mac/mod.rs +++ b/src/capturer/engine/mac/mod.rs @@ -1,6 +1,7 @@ use std::cmp; use std::sync::mpsc; +use core_video_sys::{CVPixelBufferGetPixelFormatType, CVPixelBufferRef}; use screencapturekit::{ cm_sample_buffer::CMSampleBuffer, sc_content_filter::{InitParams, SCContentFilter}, @@ -11,15 +12,27 @@ use screencapturekit::{ sc_stream_configuration::{PixelFormat, SCStreamConfiguration}, sc_types::SCFrameStatus, }; -use screencapturekit_sys::os_types::base::{CMTime, CMTimeScale}; use screencapturekit_sys::os_types::geometry::{CGPoint, CGRect, CGSize}; +use screencapturekit_sys::{ + cm_sample_buffer_ref::CMSampleBufferGetImageBuffer, + os_types::base::{CMTime, CMTimeScale}, +}; -use crate::{capturer::{Area, Options, Point, Size}, frame::BGRAFrame}; -use crate::frame::{Frame, FrameType}; use crate::targets::Target; +use crate::{ + capturer::RawCapturer, + frame::{Frame, FrameType}, +}; use crate::{capturer::Resolution, targets}; +use crate::{ + capturer::{Area, Options, Point, Size}, + frame::BGRAFrame, +}; + +use super::ChannelItem; mod apple_sys; +mod pixel_buffer; mod pixelformat; struct ErrorHandler; @@ -30,69 +43,23 @@ impl StreamErrorHandler for ErrorHandler { } pub struct Capturer { - pub tx: mpsc::Sender, + pub tx: mpsc::Sender, pub output_type: FrameType, } impl Capturer { - pub fn new(tx: mpsc::Sender, output_type: FrameType) -> Self { + pub fn new(tx: mpsc::Sender, output_type: FrameType) -> Self { Capturer { tx, output_type } } } impl StreamOutput for Capturer { fn did_output_sample_buffer(&self, sample: CMSampleBuffer, of_type: SCStreamOutputType) { - match of_type { - SCStreamOutputType::Screen => { - let frame_status = &sample.frame_status; - - match frame_status { - SCFrameStatus::Complete | SCFrameStatus::Started => unsafe { - let frame = match self.output_type { - FrameType::YUVFrame => { - let yuvframe = pixelformat::create_yuv_frame(sample).unwrap(); - Frame::YUVFrame(yuvframe) - } - FrameType::RGB => { - let rgbframe = pixelformat::create_rgb_frame(sample).unwrap(); - Frame::RGB(rgbframe) - } - FrameType::BGR0 => { - let bgrframe = pixelformat::create_bgr_frame(sample).unwrap(); - Frame::BGR0(bgrframe) - } - FrameType::BGRAFrame => { - let bgraframe = pixelformat::create_bgra_frame(sample).unwrap(); - Frame::BGRA(bgraframe) - } - }; - self.tx.send(frame).unwrap_or(()); - }, - SCFrameStatus::Idle => { - // Quick hack - just send an empty frame, and the caller can figure out how to handle it - match self.output_type { - FrameType::BGRAFrame => { - let display_time = sample.sys_ref.get_presentation_timestamp().value as u64; - let frame = BGRAFrame { - display_time, - width: 0, - height: 0, - data: vec![], - }; - self.tx.send(Frame::BGRA(frame)).unwrap_or(()); - }, - _ => {} - } - }, - _ => {} - } - } - _ => {} - } + self.tx.send((sample, of_type)).unwrap_or(()); } } -pub fn create_capturer(options: &Options, tx: mpsc::Sender) -> SCStream { +pub fn create_capturer(options: &Options, tx: mpsc::Sender) -> SCStream { // If no target is specified, capture the main display let target = options .target @@ -261,3 +228,59 @@ pub fn get_crop_area(options: &Options) -> Area { }, }) } + +pub fn process_sample_buffer( + sample: CMSampleBuffer, + of_type: SCStreamOutputType, + output_type: FrameType, +) -> Option { + match of_type { + SCStreamOutputType::Screen => { + let frame_status = &sample.frame_status; + + match frame_status { + SCFrameStatus::Complete | SCFrameStatus::Started => unsafe { + return Some(match output_type { + FrameType::YUVFrame => { + let yuvframe = pixelformat::create_yuv_frame(sample).unwrap(); + Frame::YUVFrame(yuvframe) + } + FrameType::RGB => { + let rgbframe = pixelformat::create_rgb_frame(sample).unwrap(); + Frame::RGB(rgbframe) + } + FrameType::BGR0 => { + let bgrframe = pixelformat::create_bgr_frame(sample).unwrap(); + Frame::BGR0(bgrframe) + } + FrameType::BGRAFrame => { + let bgraframe = pixelformat::create_bgra_frame(sample).unwrap(); + Frame::BGRA(bgraframe) + } + }); + }, + SCFrameStatus::Idle => { + // Quick hack - just send an empty frame, and the caller can figure out how to handle it + match output_type { + FrameType::BGRAFrame => { + // self.tx.send(sample).unwrap_or(()); + let display_time = + sample.sys_ref.get_presentation_timestamp().value as u64; + return Some(Frame::BGRA(BGRAFrame { + display_time, + width: 0, + height: 0, + data: vec![], + })); + } + _ => {} + } + } + _ => {} + } + } + _ => {} + } + + None +} diff --git a/src/capturer/engine/mac/pixel_buffer.rs b/src/capturer/engine/mac/pixel_buffer.rs new file mode 100644 index 0000000..82690c2 --- /dev/null +++ b/src/capturer/engine/mac/pixel_buffer.rs @@ -0,0 +1,192 @@ +use core::slice; +use core_video_sys::{ + CVPixelBufferGetBaseAddress, CVPixelBufferGetBaseAddressOfPlane, CVPixelBufferGetBytesPerRow, + CVPixelBufferGetBytesPerRowOfPlane, CVPixelBufferGetHeight, CVPixelBufferGetHeightOfPlane, + CVPixelBufferGetPixelFormatType, CVPixelBufferGetPlaneCount, CVPixelBufferGetWidth, + CVPixelBufferGetWidthOfPlane, CVPixelBufferLockBaseAddress, CVPixelBufferRef, + CVPixelBufferUnlockBaseAddress, +}; +use screencapturekit::{cm_sample_buffer::CMSampleBuffer, sc_types::SCFrameStatus}; +use screencapturekit_sys::cm_sample_buffer_ref::CMSampleBufferGetImageBuffer; +use std::{ops::Deref, sync::mpsc}; + +use crate::capturer::{engine::ChannelItem, RawCapturer}; + +pub struct PixelBuffer { + display_time: u64, + width: usize, + height: usize, + bytes_per_row: usize, + buffer: CMSampleBuffer, +} + +impl PixelBuffer { + pub fn display_time(&self) -> u64 { + self.display_time + } + + pub fn width(&self) -> usize { + self.width + } + + pub fn height(&self) -> usize { + self.height + } + + pub fn buffer(&self) -> &CMSampleBuffer { + &self.buffer + } + + pub fn bytes_per_row(&self) -> usize { + self.bytes_per_row + } + + pub fn data(&self) -> PixelBufferData { + unsafe { + let pixel_buffer = sample_buffer_to_pixel_buffer(&self.buffer); + + CVPixelBufferLockBaseAddress(pixel_buffer, 0); + + let base_address = CVPixelBufferGetBaseAddress(pixel_buffer); + + PixelBufferData { + buffer: pixel_buffer, + data: slice::from_raw_parts( + base_address as *mut _, + self.bytes_per_row * self.height, + ), + } + } + } + + pub fn planes(&self) -> Vec { + unsafe { + let pixel_buffer = sample_buffer_to_pixel_buffer(&self.buffer); + let count = CVPixelBufferGetPlaneCount(pixel_buffer); + + CVPixelBufferLockBaseAddress(pixel_buffer, 0); + + (0..count) + .map(|i| Plane { + buffer: pixel_buffer, + width: CVPixelBufferGetWidthOfPlane(pixel_buffer, i), + height: CVPixelBufferGetHeightOfPlane(pixel_buffer, i), + bytes_per_row: CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, i), + index: i, + }) + .collect() + } + } + + pub(crate) fn new(item: ChannelItem) -> Option { + unsafe { + let display_time = pixel_buffer_display_time(&item.0); + let pixel_buffer = sample_buffer_to_pixel_buffer(&item.0); + let (width, height) = pixel_buffer_bounds(pixel_buffer); + + match item.0.frame_status { + SCFrameStatus::Complete | SCFrameStatus::Started | SCFrameStatus::Idle => { + Some(Self { + display_time, + width, + height, + bytes_per_row: pixel_buffer_bytes_per_row(pixel_buffer), + buffer: item.0, + }) + } + _ => None, + } + } + } +} + +#[derive(Debug)] +pub struct Plane { + buffer: CVPixelBufferRef, + index: usize, + width: usize, + height: usize, + bytes_per_row: usize, +} + +impl Plane { + pub fn width(&self) -> usize { + self.width + } + + pub fn height(&self) -> usize { + self.height + } + + pub fn bytes_per_row(&self) -> usize { + self.bytes_per_row + } + + pub fn data(&self) -> PixelBufferData { + unsafe { + CVPixelBufferLockBaseAddress(self.buffer, 0); + + let base_address = CVPixelBufferGetBaseAddressOfPlane(self.buffer, self.index); + + PixelBufferData { + buffer: self.buffer, + data: slice::from_raw_parts( + base_address as *mut _, + self.bytes_per_row * self.height, + ), + } + } + } +} + +pub struct PixelBufferData<'a> { + buffer: CVPixelBufferRef, + data: &'a [u8], +} + +impl<'a> Deref for PixelBufferData<'a> { + type Target = [u8]; + + fn deref(&self) -> &'a Self::Target { + self.data + } +} + +impl<'a> Drop for PixelBufferData<'a> { + fn drop(&mut self) { + unsafe { CVPixelBufferUnlockBaseAddress(self.buffer, 0) }; + } +} + +impl RawCapturer<'_> { + #[cfg(target_os = "macos")] + pub fn get_next_pixel_buffer(&self) -> Result { + loop { + if let Some(frame) = PixelBuffer::new(self.capturer.rx.recv()?) { + return Ok(frame); + } + } + } +} + +pub unsafe fn sample_buffer_to_pixel_buffer(sample_buffer: &CMSampleBuffer) -> CVPixelBufferRef { + let buffer_ref = &(*sample_buffer.sys_ref); + let pixel_buffer = CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef; + + pixel_buffer +} + +pub unsafe fn pixel_buffer_bounds(pixel_buffer: CVPixelBufferRef) -> (usize, usize) { + let width = CVPixelBufferGetWidth(pixel_buffer); + let height = CVPixelBufferGetHeight(pixel_buffer); + + (width, height) +} + +pub unsafe fn pixel_buffer_bytes_per_row(pixel_buffer: CVPixelBufferRef) -> usize { + CVPixelBufferGetBytesPerRow(pixel_buffer) +} + +pub unsafe fn pixel_buffer_display_time(sample_buffer: &CMSampleBuffer) -> u64 { + sample_buffer.sys_ref.get_presentation_timestamp().value as u64 +} diff --git a/src/capturer/engine/mac/pixelformat.rs b/src/capturer/engine/mac/pixelformat.rs index 911bb3c..4623e1e 100644 --- a/src/capturer/engine/mac/pixelformat.rs +++ b/src/capturer/engine/mac/pixelformat.rs @@ -1,11 +1,14 @@ use std::{mem, slice}; -use screencapturekit::cm_sample_buffer::CMSampleBuffer; +use screencapturekit::{cm_sample_buffer::CMSampleBuffer, cv_pixel_buffer::CVPixelBuffer}; use screencapturekit_sys::cm_sample_buffer_ref::{ CMSampleBufferGetImageBuffer, CMSampleBufferGetSampleAttachmentsArray, }; -use super::apple_sys::*; +use super::{ + apple_sys::*, + pixel_buffer::{pixel_buffer_bounds, sample_buffer_to_pixel_buffer}, +}; use crate::frame::{ convert_bgra_to_rgb, get_cropped_data, remove_alpha_channel, BGRAFrame, BGRFrame, RGBFrame, YUVFrame, @@ -13,8 +16,8 @@ use crate::frame::{ use core_graphics_helmer_fork::display::{CFArrayGetCount, CFArrayGetValueAtIndex, CFArrayRef}; use core_video_sys::{ CVPixelBufferGetBaseAddress, CVPixelBufferGetBaseAddressOfPlane, CVPixelBufferGetBytesPerRow, - CVPixelBufferGetBytesPerRowOfPlane, CVPixelBufferGetHeight, CVPixelBufferGetWidth, - CVPixelBufferLockBaseAddress, CVPixelBufferRef, CVPixelBufferUnlockBaseAddress, + CVPixelBufferGetBytesPerRowOfPlane, CVPixelBufferLockBaseAddress, + CVPixelBufferUnlockBaseAddress, }; pub unsafe fn create_yuv_frame(sample_buffer: CMSampleBuffer) -> Option { @@ -49,12 +52,11 @@ pub unsafe fn create_yuv_frame(sample_buffer: CMSampleBuffer) -> Option Option Option { - let buffer_ref = &(*sample_buffer.sys_ref); let epoch = sample_buffer.sys_ref.get_presentation_timestamp().value; - let pixel_buffer = CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef; + let pixel_buffer = sample_buffer_to_pixel_buffer(&sample_buffer); CVPixelBufferLockBaseAddress(pixel_buffer, 0); - let width = CVPixelBufferGetWidth(pixel_buffer); - let height = CVPixelBufferGetHeight(pixel_buffer); + let (width, height) = pixel_buffer_bounds(pixel_buffer); if width == 0 || height == 0 { return None; } @@ -125,14 +125,12 @@ pub unsafe fn create_bgr_frame(sample_buffer: CMSampleBuffer) -> Option Option { - let buffer_ref = &(*sample_buffer.sys_ref); let epoch = sample_buffer.sys_ref.get_presentation_timestamp().value; - let pixel_buffer = CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef; + let pixel_buffer = sample_buffer_to_pixel_buffer(&sample_buffer); CVPixelBufferLockBaseAddress(pixel_buffer, 0); - let width = CVPixelBufferGetWidth(pixel_buffer); - let height = CVPixelBufferGetHeight(pixel_buffer); + let (width, height) = pixel_buffer_bounds(pixel_buffer); if width == 0 || height == 0 { return None; } @@ -141,6 +139,7 @@ pub unsafe fn create_bgra_frame(sample_buffer: CMSampleBuffer) -> Option = vec![]; + for i in 0..height { let start = (base_address as *mut u8).wrapping_add(i * bytes_per_row); data.extend_from_slice(slice::from_raw_parts(start, 4 * width)); @@ -157,14 +156,12 @@ pub unsafe fn create_bgra_frame(sample_buffer: CMSampleBuffer) -> Option Option { - let buffer_ref = &(*sample_buffer.sys_ref); let epoch = sample_buffer.sys_ref.get_presentation_timestamp().value; - let pixel_buffer = CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef; + let pixel_buffer = sample_buffer_to_pixel_buffer(&sample_buffer); CVPixelBufferLockBaseAddress(pixel_buffer, 0); - let width = CVPixelBufferGetWidth(pixel_buffer); - let height = CVPixelBufferGetHeight(pixel_buffer); + let (width, height) = pixel_buffer_bounds(pixel_buffer); if width == 0 || height == 0 { return None; } @@ -189,5 +186,4 @@ pub unsafe fn create_rgb_frame(sample_buffer: CMSampleBuffer) -> Option [u32; 2] { + #[cfg(target_os = "macos")] + { + mac::get_output_frame_size(options) + } + + #[cfg(target_os = "windows")] + { + win::get_output_frame_size(options) + } + + #[cfg(target_os = "linux")] + { + // TODO: How to calculate this on Linux? + return [0, 0]; + } +} + pub struct Engine { options: Options, #[cfg(target_os = "macos")] @@ -25,7 +51,7 @@ pub struct Engine { } impl Engine { - pub fn new(options: &Options, tx: mpsc::Sender) -> Engine { + pub fn new(options: &Options, tx: mpsc::Sender) -> Engine { #[cfg(target_os = "macos")] { let mac = mac::create_capturer(&options, tx); @@ -90,19 +116,15 @@ impl Engine { } pub fn get_output_frame_size(&mut self) -> [u32; 2] { - #[cfg(target_os = "macos")] - { - mac::get_output_frame_size(&self.options) - } - - #[cfg(target_os = "windows")] - { - win::get_output_frame_size(&self.options) - } + get_output_frame_size(&self.options) + } - #[cfg(target_os = "linux")] + pub fn process_channel_item(&self, data: ChannelItem) -> Option { + #[cfg(target_os = "macos")] { - return [0, 0]; + mac::process_sample_buffer(data.0, data.1, self.options.output_type) } + #[cfg(not(target_os = "macos"))] + return Some(data); } } diff --git a/src/capturer/mod.rs b/src/capturer/mod.rs index 7624389..2ad23d3 100644 --- a/src/capturer/mod.rs +++ b/src/capturer/mod.rs @@ -2,12 +2,16 @@ mod engine; use std::sync::mpsc; +use engine::ChannelItem; + use crate::{ frame::{Frame, FrameType}, - has_permission, is_supported, request_permission, + has_permission, is_supported, targets::Target, }; +pub use engine::get_output_frame_size; + #[derive(Debug, Clone, Copy, Default)] pub enum Resolution { _480p, @@ -71,7 +75,7 @@ pub struct Options { /// Screen capturer class pub struct Capturer { engine: engine::Engine, - rx: mpsc::Receiver, + rx: mpsc::Receiver, } pub enum CapturerBuildError { @@ -86,7 +90,7 @@ impl Capturer { note = "Use `build` instead of `new` to create a new capturer instance." )] pub fn new(options: Options) -> Capturer { - let (tx, rx) = mpsc::channel::(); + let (tx, rx) = mpsc::channel(); let engine = engine::Engine::new(&options, tx); Capturer { engine, rx } @@ -102,7 +106,7 @@ impl Capturer { return Err(CapturerBuildError::PermissionNotGranted); } - let (tx, rx) = mpsc::channel::(); + let (tx, rx) = mpsc::channel(); let engine = engine::Engine::new(&options, tx); Ok(Capturer { engine, rx }) @@ -122,11 +126,25 @@ impl Capturer { /// Get the next captured frame pub fn get_next_frame(&self) -> Result { - self.rx.recv() + loop { + let res = self.rx.recv()?; + + if let Some(frame) = self.engine.process_channel_item(res) { + return Ok(frame); + } + } } /// Get the dimensions the frames will be captured in pub fn get_output_frame_size(&mut self) -> [u32; 2] { self.engine.get_output_frame_size() } + + pub fn raw(&self) -> RawCapturer { + RawCapturer { capturer: self } + } +} + +pub struct RawCapturer<'a> { + capturer: &'a Capturer, } From 6bb55a29a1d1d86cabb83b5a88f090a3804c53b2 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 7 Nov 2024 12:42:31 +0800 Subject: [PATCH 2/3] imports --- src/capturer/engine/mac/pixel_buffer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/capturer/engine/mac/pixel_buffer.rs b/src/capturer/engine/mac/pixel_buffer.rs index 82690c2..70bbae9 100644 --- a/src/capturer/engine/mac/pixel_buffer.rs +++ b/src/capturer/engine/mac/pixel_buffer.rs @@ -2,9 +2,8 @@ use core::slice; use core_video_sys::{ CVPixelBufferGetBaseAddress, CVPixelBufferGetBaseAddressOfPlane, CVPixelBufferGetBytesPerRow, CVPixelBufferGetBytesPerRowOfPlane, CVPixelBufferGetHeight, CVPixelBufferGetHeightOfPlane, - CVPixelBufferGetPixelFormatType, CVPixelBufferGetPlaneCount, CVPixelBufferGetWidth, - CVPixelBufferGetWidthOfPlane, CVPixelBufferLockBaseAddress, CVPixelBufferRef, - CVPixelBufferUnlockBaseAddress, + CVPixelBufferGetPlaneCount, CVPixelBufferGetWidth, CVPixelBufferGetWidthOfPlane, + CVPixelBufferLockBaseAddress, CVPixelBufferRef, CVPixelBufferUnlockBaseAddress, }; use screencapturekit::{cm_sample_buffer::CMSampleBuffer, sc_types::SCFrameStatus}; use screencapturekit_sys::cm_sample_buffer_ref::CMSampleBufferGetImageBuffer; From 88d0e14d1606fc6f0a7f3b828f16aefaa91b159c Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 7 Nov 2024 12:54:45 +0800 Subject: [PATCH 3/3] clippy --- src/capturer/engine/mac/apple_sys.rs | 2 + src/capturer/engine/mac/mod.rs | 103 ++++++++++-------------- src/capturer/engine/mac/pixel_buffer.rs | 4 +- src/capturer/engine/mac/pixelformat.rs | 8 +- src/capturer/engine/mod.rs | 8 +- src/capturer/mod.rs | 28 +++++-- src/frame/mod.rs | 12 +-- src/utils/mac/mod.rs | 4 +- 8 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/capturer/engine/mac/apple_sys.rs b/src/capturer/engine/mac/apple_sys.rs index 7ecf3b4..349f268 100644 --- a/src/capturer/engine/mac/apple_sys.rs +++ b/src/capturer/engine/mac/apple_sys.rs @@ -1,3 +1,5 @@ +#![allow(non_upper_case_globals)] + pub use screencapturekit_sys::os_types::base::*; #[repr(C)] diff --git a/src/capturer/engine/mac/mod.rs b/src/capturer/engine/mac/mod.rs index b76e4fa..41f0ba0 100644 --- a/src/capturer/engine/mac/mod.rs +++ b/src/capturer/engine/mac/mod.rs @@ -1,7 +1,6 @@ use std::cmp; use std::sync::mpsc; -use core_video_sys::{CVPixelBufferGetPixelFormatType, CVPixelBufferRef}; use pixelformat::get_pts_in_nanoseconds; use screencapturekit::{ cm_sample_buffer::CMSampleBuffer, @@ -13,20 +12,13 @@ use screencapturekit::{ sc_stream_configuration::{PixelFormat, SCStreamConfiguration}, sc_types::SCFrameStatus, }; +use screencapturekit_sys::os_types::base::{CMTime, CMTimeScale}; use screencapturekit_sys::os_types::geometry::{CGPoint, CGRect, CGSize}; -use screencapturekit_sys::{ - cm_sample_buffer_ref::CMSampleBufferGetImageBuffer, - os_types::base::{CMTime, CMTimeScale}, -}; use crate::frame::{Frame, FrameType}; use crate::targets::Target; use crate::{ - capturer::RawCapturer, - frame::{Frame, FrameType}, -}; -use crate::{ - capturer::{Area, Options, Point, RawCapturer, Resolution, Size}, + capturer::{Area, Options, Point, Resolution, Size}, frame::BGRAFrame, targets, }; @@ -46,12 +38,11 @@ impl StreamErrorHandler for ErrorHandler { pub struct Capturer { pub tx: mpsc::Sender, - pub output_type: FrameType, } impl Capturer { - pub fn new(tx: mpsc::Sender, output_type: FrameType) -> Self { - Capturer { tx, output_type } + pub fn new(tx: mpsc::Sender) -> Self { + Capturer { tx } } } @@ -99,14 +90,13 @@ pub fn create_capturer(options: &Options, tx: mpsc::Sender) -> SCSt .into_iter() .filter(|window| { excluded_targets - .into_iter() - .find(|excluded_target| match excluded_target { + .iter() + .any(|excluded_target| match excluded_target { Target::Window(excluded_window) => { excluded_window.id == window.window_id } _ => false, }) - .is_some() }) .collect(); @@ -156,10 +146,7 @@ pub fn create_capturer(options: &Options, tx: mpsc::Sender) -> SCSt }; let mut stream = SCStream::new(filter, stream_config, ErrorHandler); - stream.add_output( - Capturer::new(tx, options.output_type), - SCStreamOutputType::Screen, - ); + stream.add_output(Capturer::new(tx), SCStreamOutputType::Screen); stream } @@ -217,8 +204,8 @@ pub fn get_crop_area(options: &Options) -> Area { y: val.origin.y, }, size: Size { - width: input_width as f64, - height: input_height as f64, + width: input_width, + height: input_height, }, } }) @@ -236,49 +223,43 @@ pub fn process_sample_buffer( of_type: SCStreamOutputType, output_type: FrameType, ) -> Option { - match of_type { - SCStreamOutputType::Screen => { - let frame_status = &sample.frame_status; - - match frame_status { - SCFrameStatus::Complete | SCFrameStatus::Started => unsafe { - return Some(match output_type { - FrameType::YUVFrame => { - let yuvframe = pixelformat::create_yuv_frame(sample).unwrap(); - Frame::YUVFrame(yuvframe) - } - FrameType::RGB => { - let rgbframe = pixelformat::create_rgb_frame(sample).unwrap(); - Frame::RGB(rgbframe) - } - FrameType::BGR0 => { - let bgrframe = pixelformat::create_bgr_frame(sample).unwrap(); - Frame::BGR0(bgrframe) - } - FrameType::BGRAFrame => { - let bgraframe = pixelformat::create_bgra_frame(sample).unwrap(); - Frame::BGRA(bgraframe) - } - }); - }, - SCFrameStatus::Idle => { - // Quick hack - just send an empty frame, and the caller can figure out how to handle it - match output_type { - FrameType::BGRAFrame => { - return Some(Frame::BGRA(BGRAFrame { - display_time: get_pts_in_nanoseconds(&sample), - width: 0, - height: 0, - data: vec![], - })); - } - _ => {} + if let SCStreamOutputType::Screen = of_type { + let frame_status = &sample.frame_status; + + match frame_status { + SCFrameStatus::Complete | SCFrameStatus::Started => unsafe { + return Some(match output_type { + FrameType::YUVFrame => { + let yuvframe = pixelformat::create_yuv_frame(sample).unwrap(); + Frame::YUVFrame(yuvframe) } + FrameType::RGB => { + let rgbframe = pixelformat::create_rgb_frame(sample).unwrap(); + Frame::RGB(rgbframe) + } + FrameType::BGR0 => { + let bgrframe = pixelformat::create_bgr_frame(sample).unwrap(); + Frame::BGR0(bgrframe) + } + FrameType::BGRAFrame => { + let bgraframe = pixelformat::create_bgra_frame(sample).unwrap(); + Frame::BGRA(bgraframe) + } + }); + }, + SCFrameStatus::Idle => { + // Quick hack - just send an empty frame, and the caller can figure out how to handle it + if let FrameType::BGRAFrame = output_type { + return Some(Frame::BGRA(BGRAFrame { + display_time: get_pts_in_nanoseconds(&sample), + width: 0, + height: 0, + data: vec![], + })); } - _ => {} } + _ => {} } - _ => {} } None diff --git a/src/capturer/engine/mac/pixel_buffer.rs b/src/capturer/engine/mac/pixel_buffer.rs index 70bbae9..af6956f 100644 --- a/src/capturer/engine/mac/pixel_buffer.rs +++ b/src/capturer/engine/mac/pixel_buffer.rs @@ -170,9 +170,9 @@ impl RawCapturer<'_> { pub unsafe fn sample_buffer_to_pixel_buffer(sample_buffer: &CMSampleBuffer) -> CVPixelBufferRef { let buffer_ref = &(*sample_buffer.sys_ref); - let pixel_buffer = CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef; + - pixel_buffer + CMSampleBufferGetImageBuffer(buffer_ref) as CVPixelBufferRef } pub unsafe fn pixel_buffer_bounds(pixel_buffer: CVPixelBufferRef) -> (usize, usize) { diff --git a/src/capturer/engine/mac/pixelformat.rs b/src/capturer/engine/mac/pixelformat.rs index 7ce0705..949e4f5 100644 --- a/src/capturer/engine/mac/pixelformat.rs +++ b/src/capturer/engine/mac/pixelformat.rs @@ -1,9 +1,7 @@ use std::{mem, slice}; -use screencapturekit::{cm_sample_buffer::CMSampleBuffer, cv_pixel_buffer::CVPixelBuffer}; -use screencapturekit_sys::cm_sample_buffer_ref::{ - CMSampleBufferGetImageBuffer, CMSampleBufferGetSampleAttachmentsArray, -}; +use screencapturekit::cm_sample_buffer::CMSampleBuffer; +use screencapturekit_sys::cm_sample_buffer_ref::CMSampleBufferGetSampleAttachmentsArray; use super::{ apple_sys::*, @@ -61,7 +59,7 @@ pub unsafe fn create_yuv_frame(sample_buffer: CMSampleBuffer) -> Option) -> Engine { #[cfg(target_os = "macos")] { - let mac = mac::create_capturer(&options, tx); - return Engine { + let mac = mac::create_capturer(options, tx); + Engine { mac, options: (*options).clone(), - }; + } } #[cfg(target_os = "windows")] diff --git a/src/capturer/mod.rs b/src/capturer/mod.rs index 2ad23d3..9e3f9ab 100644 --- a/src/capturer/mod.rs +++ b/src/capturer/mod.rs @@ -1,6 +1,6 @@ mod engine; -use std::sync::mpsc; +use std::{error::Error, sync::mpsc}; use engine::ChannelItem; @@ -28,12 +28,12 @@ pub enum Resolution { impl Resolution { fn value(&self, aspect_ratio: f32) -> [u32; 2] { match *self { - Resolution::_480p => [640, ((640 as f32) / aspect_ratio).floor() as u32], - Resolution::_720p => [1280, ((1280 as f32) / aspect_ratio).floor() as u32], - Resolution::_1080p => [1920, ((1920 as f32) / aspect_ratio).floor() as u32], - Resolution::_1440p => [2560, ((2560 as f32) / aspect_ratio).floor() as u32], - Resolution::_2160p => [3840, ((3840 as f32) / aspect_ratio).floor() as u32], - Resolution::_4320p => [7680, ((7680 as f32) / aspect_ratio).floor() as u32], + Resolution::_480p => [640, (640_f32 / aspect_ratio).floor() as u32], + Resolution::_720p => [1280, (1280_f32 / aspect_ratio).floor() as u32], + Resolution::_1080p => [1920, (1920_f32 / aspect_ratio).floor() as u32], + Resolution::_1440p => [2560, (2560_f32 / aspect_ratio).floor() as u32], + Resolution::_2160p => [3840, (3840_f32 / aspect_ratio).floor() as u32], + Resolution::_4320p => [7680, (7680_f32 / aspect_ratio).floor() as u32], Resolution::Captured => { panic!(".value should not be called when Resolution type is Captured") } @@ -78,11 +78,25 @@ pub struct Capturer { rx: mpsc::Receiver, } +#[derive(Debug)] pub enum CapturerBuildError { NotSupported, PermissionNotGranted, } +impl std::fmt::Display for CapturerBuildError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CapturerBuildError::NotSupported => write!(f, "Screen capturing is not supported"), + CapturerBuildError::PermissionNotGranted => { + write!(f, "Permission to capture the screen is not granted") + } + } + } +} + +impl Error for CapturerBuildError {} + impl Capturer { /// Create a new capturer instance with the provided options #[deprecated( diff --git a/src/frame/mod.rs b/src/frame/mod.rs index cd37e08..77797b7 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -101,7 +101,7 @@ pub fn remove_alpha_channel(frame_data: Vec) -> Vec { dst[2] = src[2]; } - return data; + data } pub fn convert_bgra_to_rgb(frame_data: Vec) -> Vec { @@ -116,24 +116,24 @@ pub fn convert_bgra_to_rgb(frame_data: Vec) -> Vec { dst[2] = src[0]; } - return data; + data } pub fn get_cropped_data(data: Vec, cur_width: i32, height: i32, width: i32) -> Vec { if data.len() as i32 != height * cur_width * 4 { - return data; + data } else { let mut cropped_data: Vec = vec![0; (4 * height * width).try_into().unwrap()]; let mut cropped_data_index = 0; - for i in 0..data.len() { + for (i, item) in data.iter().enumerate() { let x = i as i32 % (cur_width * 4); if x < (width * 4) { - cropped_data[cropped_data_index] = data[i]; + cropped_data[cropped_data_index] = *item; cropped_data_index += 1; } } - return cropped_data; + cropped_data } } diff --git a/src/utils/mac/mod.rs b/src/utils/mac/mod.rs index afb7f73..ae3adc4 100644 --- a/src/utils/mac/mod.rs +++ b/src/utils/mac/mod.rs @@ -2,11 +2,11 @@ use core_graphics_helmer_fork::access::ScreenCaptureAccess; use sysinfo::System; pub fn has_permission() -> bool { - ScreenCaptureAccess::default().preflight() + ScreenCaptureAccess.preflight() } pub fn request_permission() -> bool { - ScreenCaptureAccess::default().request() + ScreenCaptureAccess.request() } pub fn is_supported() -> bool {