diff --git a/src/capture_stream.rs b/src/capture_stream.rs index ab1ed215..3ff38332 100644 --- a/src/capture_stream.rs +++ b/src/capture_stream.rs @@ -171,6 +171,7 @@ pub struct CaptureConfig { pub(crate) capture_audio: Option, pub(crate) impl_capture_config: ImplCaptureConfig, pub(crate) buffer_count: usize, + pub(crate) color_space: Option } /// Represents an error creating the capture config @@ -221,6 +222,7 @@ impl CaptureConfig { impl_capture_config: ImplCaptureConfig::new(), capture_audio: None, buffer_count: 3, + color_space: None, }) } @@ -235,6 +237,7 @@ impl CaptureConfig { impl_capture_config: ImplCaptureConfig::new(), capture_audio: None, buffer_count: 3, + color_space: None, } } @@ -263,6 +266,13 @@ impl CaptureConfig { ..self } } + + pub fn with_color_space_name(self, color_space_name: String) -> Self { + Self { + color_space: Some(color_space_name), + ..self + } + } } /// Represents an active capture stream diff --git a/src/feature/screenshot/platform/macos.rs b/src/feature/screenshot/platform/macos.rs index d0e79cee..d00dc707 100644 --- a/src/feature/screenshot/platform/macos.rs +++ b/src/feature/screenshot/platform/macos.rs @@ -4,7 +4,7 @@ use std::time::Instant; use crate::feature::screenshot::ScreenshotError; use crate::frame::VideoFrame; use crate::platform::macos::frame::{MacosSCStreamVideoFrame, MacosVideoFrame}; -use crate::platform::macos::objc_wrap::{CGSize, NSArray, SCContentFilter, SCScreenshotManager, SCStreamColorMatrix, SCStreamConfiguration, SCStreamPixelFormat}; +use crate::platform::macos::objc_wrap::{CGSize, NSArray, SCContentFilter, SCScreenshotManager, SCStreamColorMatrix, SCStreamColorSpaceName, SCStreamConfiguration, SCStreamPixelFormat}; use crate::platform::platform_impl::objc_wrap::{CGMainDisplayID, CMTime, DispatchQueue, SCStream, SCStreamCallbackError, SCStreamHandler, SCStreamOutputType}; use crate::prelude::{Capturable, CaptureAccessToken, CaptureConfig, CapturePixelFormat}; @@ -28,6 +28,9 @@ pub async fn take_screenshot(token: CaptureAccessToken, config: CaptureConfig) - if set_color_matrix { stream_config.set_color_matrix(SCStreamColorMatrix::ItuR709_2); } + if let Some(color_space) = config.color_space { + stream_config.set_color_space(SCStreamColorSpaceName::from_string(color_space)); + } stream_config.set_pixel_format(pixel_format); stream_config.set_size(CGSize { x: config.output_size.width, diff --git a/src/platform/macos/capture_stream.rs b/src/platform/macos/capture_stream.rs index 47a2fac4..4cd40adc 100644 --- a/src/platform/macos/capture_stream.rs +++ b/src/platform/macos/capture_stream.rs @@ -5,7 +5,7 @@ use objc2::runtime::AnyObject; use parking_lot::Mutex; use crate::{capture_stream::{CaptureConfig, StreamCreateError, StreamError, StreamEvent}, platform::platform_impl::{frame::MacosSCStreamVideoFrame, objc_wrap::NSNumber}, prelude::{AudioCaptureConfig, AudioFrame, Capturable, CaptureConfigError, CapturePixelFormat, Point, StreamStopError, VideoFrame}, util::{Rect, Size}}; -use super::{frame::{MacosAudioFrame, MacosCGDisplayStreamVideoFrame, MacosVideoFrame}, objc_wrap::{kCFBooleanFalse, kCFBooleanTrue, kCGDisplayStreamDestinationRect, kCGDisplayStreamMinimumFrameTime, kCGDisplayStreamPreserveAspectRatio, kCGDisplayStreamQueueDepth, kCGDisplayStreamShowCursor, kCGDisplayStreamSourceRect, CFNumber, CGDisplayStream, CGDisplayStreamFrameStatus, CGPoint, CGRect, CGSize, CMSampleBuffer, CMTime, DispatchQueue, IOSurface, NSArray, NSDictionary, NSString, SCCaptureResolutionType, SCContentFilter, SCFrameStatus, SCStream, SCStreamCallbackError, SCStreamColorMatrix, SCStreamConfiguration, SCStreamFrameInfoStatus, SCStreamHandler, SCStreamOutputType, SCStreamPixelFormat, SCStreamSampleRate}}; +use super::{frame::{MacosAudioFrame, MacosCGDisplayStreamVideoFrame, MacosVideoFrame}, objc_wrap::{kCFBooleanFalse, kCFBooleanTrue, kCGDisplayStreamDestinationRect, kCGDisplayStreamMinimumFrameTime, kCGDisplayStreamPreserveAspectRatio, kCGDisplayStreamQueueDepth, kCGDisplayStreamShowCursor, kCGDisplayStreamSourceRect, CFNumber, CGDisplayStream, CGDisplayStreamFrameStatus, CGPoint, CGRect, CGSize, CMSampleBuffer, CMTime, DispatchQueue, IOSurface, NSArray, NSDictionary, NSString, SCCaptureResolutionType, SCContentFilter, SCFrameStatus, SCStream, SCStreamCallbackError, SCStreamColorMatrix, SCStreamColorSpaceName, SCStreamConfiguration, SCStreamFrameInfoStatus, SCStreamHandler, SCStreamOutputType, SCStreamPixelFormat, SCStreamSampleRate}}; pub type MacosPixelFormat = SCStreamPixelFormat; @@ -234,6 +234,10 @@ impl MacosCaptureStream { if set_color_matrix { config.set_color_matrix(SCStreamColorMatrix::ItuR709_2); } + + if let Some(color_space)= capture_config.color_space { + config.set_color_space(SCStreamColorSpaceName::from_string(color_space)); + } config.set_pixel_format(pixel_format); config.set_minimum_time_interval(CMTime::new_with_seconds(capture_config.impl_capture_config.maximum_fps.map(|x| 1.0 / x).unwrap_or(1.0 / 120.0) as f64, 240)); /*config.set_source_rect(CGRect { diff --git a/src/platform/macos/objc_wrap.rs b/src/platform/macos/objc_wrap.rs index 268d377c..96e2d6f2 100644 --- a/src/platform/macos/objc_wrap.rs +++ b/src/platform/macos/objc_wrap.rs @@ -218,6 +218,31 @@ extern "C" { static kCGDisplayStreamYCbCrMatrix_ITU_R_601_4 : CFStringRef; static kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995 : CFStringRef; + static kCGColorSpaceDisplayP3: CFStringRef; + static kCGColorSpaceDisplayP3_HLG: CFStringRef; + static kCGColorSpaceExtendedLinearDisplayP3: CFStringRef; + static kCGColorSpaceSRGB : CFStringRef; + static kCGColorSpaceLinearSRGB: CFStringRef; + static kCGColorSpaceExtendedSRGB: CFStringRef; + static kCGColorSpaceExtendedLinearSRGB: CFStringRef; + static kCGColorSpaceGenericGrayGamma2_2: CFStringRef; + static kCGColorSpaceLinearGray: CFStringRef; + static kCGColorSpaceExtendedGray: CFStringRef; + static kCGColorSpaceExtendedLinearGray: CFStringRef; + static kCGColorSpaceGenericRGBLinear: CFStringRef; + static kCGColorSpaceGenericCMYK: CFStringRef; + static kCGColorSpaceGenericXYZ: CFStringRef; + static kCGColorSpaceGenericLab: CFStringRef; + static kCGColorSpaceACESCGLinear: CFStringRef; + static kCGColorSpaceAdobeRGB1998: CFStringRef; + static kCGColorSpaceDCIP3: CFStringRef; + static kCGColorSpaceITUR_709: CFStringRef; + static kCGColorSpaceROMMRGB: CFStringRef; + static kCGColorSpaceGenericGray: CFStringRef; + static kCGColorSpaceGenericRGB: CFStringRef; + static kCGColorSpaceExtendedLinearITUR_2020: CFStringRef; + static kCGColorSpaceITUR_2020: CFStringRef; + static NSDeviceSize: CFStringRef; pub(crate) static CGRectNull : CGRect; @@ -855,6 +880,97 @@ impl SCStreamColorMatrix { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum SCStreamColorSpaceName { + KCgcolorSpaceDisplayP3, + KCgcolorSpaceDisplayP3Hlg, + KCgcolorSpaceExtendedLinearDisplayP3, + KCgcolorSpaceSrgb, + KCgcolorSpaceLinearSrgb, + KCgcolorSpaceExtendedSrgb, + KCgcolorSpaceExtendedLinearSrgb, + KCgcolorSpaceGenericGrayGamma2_2, + KCgcolorSpaceLinearGray, + KCgcolorSpaceExtendedGray, + KCgcolorSpaceExtendedLinearGray, + KCgcolorSpaceGenericRgblinear, + KCgcolorSpaceGenericCmyk, + KCgcolorSpaceGenericXyz, + KCgcolorSpaceGenericLab, + KCgcolorSpaceAcescglinear, + KCgcolorSpaceAdobeRgb1998, + KCgcolorSpaceDcip3, + KCgcolorSpaceItur709, + KCgcolorSpaceRommrgb, + KCgcolorSpaceGenericGray, + KCgcolorSpaceGenericRgb, + KCgcolorSpaceExtendedLinearItur2020, + KCgcolorSpaceItur2020, +} + +impl SCStreamColorSpaceName { + pub(crate) fn to_cfstringref(&self) -> CFStringRef { + unsafe { + match self { + Self::KCgcolorSpaceDisplayP3 => kCGColorSpaceDisplayP3, + Self::KCgcolorSpaceDisplayP3Hlg => kCGColorSpaceDisplayP3_HLG, + Self::KCgcolorSpaceExtendedLinearDisplayP3 => kCGColorSpaceExtendedLinearDisplayP3, + Self::KCgcolorSpaceSrgb => kCGColorSpaceSRGB, + Self::KCgcolorSpaceLinearSrgb => kCGColorSpaceExtendedLinearSRGB, + Self::KCgcolorSpaceExtendedSrgb => kCGColorSpaceExtendedSRGB, + Self::KCgcolorSpaceExtendedLinearSrgb => kCGColorSpaceExtendedLinearSRGB, + Self::KCgcolorSpaceGenericGrayGamma2_2 => kCGColorSpaceGenericGrayGamma2_2, + Self::KCgcolorSpaceLinearGray => kCGColorSpaceLinearGray, + Self::KCgcolorSpaceExtendedGray => kCGColorSpaceExtendedGray, + Self::KCgcolorSpaceExtendedLinearGray => kCGColorSpaceExtendedLinearGray, + Self::KCgcolorSpaceGenericRgblinear => kCGColorSpaceGenericRGBLinear, + Self::KCgcolorSpaceGenericCmyk => kCGColorSpaceGenericCMYK, + Self::KCgcolorSpaceGenericXyz => kCGColorSpaceGenericXYZ, + Self::KCgcolorSpaceGenericLab => kCGColorSpaceGenericLab, + Self::KCgcolorSpaceAcescglinear => kCGColorSpaceACESCGLinear, + Self::KCgcolorSpaceAdobeRgb1998 => kCGColorSpaceAdobeRGB1998, + Self::KCgcolorSpaceDcip3 => kCGColorSpaceDCIP3, + Self::KCgcolorSpaceItur709 => kCGColorSpaceITUR_709, + Self::KCgcolorSpaceRommrgb => kCGColorSpaceROMMRGB, + Self::KCgcolorSpaceGenericGray => kCGColorSpaceGenericGray, + Self::KCgcolorSpaceGenericRgb => kCGColorSpaceGenericRGB, + Self::KCgcolorSpaceExtendedLinearItur2020 => kCGColorSpaceExtendedLinearITUR_2020, + Self::KCgcolorSpaceItur2020 => kCGColorSpaceITUR_2020, + } + } + } + + pub fn from_string(color_space_name: String) -> Self { + match color_space_name.as_str() { + "KCgcolorSrgb" => Self::KCgcolorSpaceSrgb, + "KCgcolorSpaceDisplayP3Hlg" => Self::KCgcolorSpaceDisplayP3Hlg, + "KCgcolorSpaceExtendedLinearDisplayP3" => Self::KCgcolorSpaceExtendedLinearDisplayP3, + "KCgcolorSpaceAcescglinear" => Self::KCgcolorSpaceAcescglinear, + "KCgcolorSpaceAdobeRgb1998" => Self::KCgcolorSpaceAdobeRgb1998, + "KCgcolorSpaceDcip3" => Self::KCgcolorSpaceDcip3, + "KCgcolorSpaceExtendedGray" => Self::KCgcolorSpaceExtendedGray, + "KCgcolorSpaceExtendedLinearGray" => Self::KCgcolorSpaceExtendedLinearGray, + "KCgcolorSpaceExtendedLinearItur2020" => Self::KCgcolorSpaceExtendedLinearItur2020, + "KCgcolorSpaceExtendedLinearSrgb" => Self::KCgcolorSpaceExtendedLinearSrgb, + "KCgcolorSpaceExtendedSrgb" => Self::KCgcolorSpaceExtendedSrgb, + "KCgcolorSpaceGenericCmyk" => Self::KCgcolorSpaceGenericCmyk, + "KCgcolorSpaceGenericGray" => Self::KCgcolorSpaceGenericGray, + "KCgcolorSpaceGenericGrayGamma2_2" => Self::KCgcolorSpaceGenericGrayGamma2_2, + "KCgcolorSpaceGenericLab" => Self::KCgcolorSpaceGenericLab, + "KCgcolorSpaceGenericRgb" => Self::KCgcolorSpaceGenericRgb, + "KCgcolorSpaceGenericRgblinear" => Self::KCgcolorSpaceGenericRgblinear, + "KCgcolorSpaceGenericXyz" => Self::KCgcolorSpaceGenericXyz, + "KCgcolorSpaceItur2020" => Self::KCgcolorSpaceItur2020, + "KCgcolorSpaceItur709" => Self::KCgcolorSpaceItur709, + "KCgcolorSpaceLinearGray" => Self::KCgcolorSpaceLinearGray, + "KCgcolorSpaceLinearSrgb" => Self::KCgcolorSpaceLinearSrgb, + "KCgcolorSpaceRommrgb" => Self::KCgcolorSpaceRommrgb, + "KCgcolorSpaceSrgb" => Self::KCgcolorSpaceSrgb, + default => Self::KCgcolorSpaceSrgb + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SCCaptureResolutionType { SCCaptureResolutionAutomatic = 0, @@ -919,6 +1035,12 @@ impl SCStreamConfiguration { } } + pub(crate) fn set_color_space(&mut self, color_space: SCStreamColorSpaceName) { + unsafe { + let _: () = msg_send![self.0, setColorSpaceName: CFStringRefEncoded(color_space.to_cfstringref())]; + } + } + pub(crate) fn set_resolution_type(&mut self, resolution_type: SCCaptureResolutionType) -> Result<(), ()> { unsafe { let has_property: Bool = msg_send![self.0, respondsToSelector: sel!(setCaptureResolution:)];