source code

This commit is contained in:
rustdesk
2021-03-29 15:59:14 +08:00
parent 002fce136c
commit d1013487e2
175 changed files with 35074 additions and 2 deletions

View File

@@ -0,0 +1,111 @@
use std::ptr;
use block::{Block, ConcreteBlock};
use libc::c_void;
use std::sync::{Arc, Mutex};
use super::config::Config;
use super::display::Display;
use super::ffi::*;
use super::frame::Frame;
pub struct Capturer {
stream: CGDisplayStreamRef,
queue: DispatchQueue,
width: usize,
height: usize,
format: PixelFormat,
display: Display,
stopped: Arc<Mutex<bool>>,
}
impl Capturer {
pub fn new<F: Fn(Frame) + 'static>(
display: Display,
width: usize,
height: usize,
format: PixelFormat,
config: Config,
handler: F,
) -> Result<Capturer, CGError> {
let stopped = Arc::new(Mutex::new(false));
let cloned_stopped = stopped.clone();
let handler: FrameAvailableHandler = ConcreteBlock::new(move |status, _, surface, _| {
use self::CGDisplayStreamFrameStatus::*;
if status == Stopped {
let mut lock = cloned_stopped.lock().unwrap();
*lock = true;
return;
}
if status == FrameComplete {
handler(unsafe { Frame::new(surface) });
}
})
.copy();
let queue = unsafe {
dispatch_queue_create(
b"quadrupleslap.scrap\0".as_ptr() as *const i8,
ptr::null_mut(),
)
};
let stream = unsafe {
let config = config.build();
let stream = CGDisplayStreamCreateWithDispatchQueue(
display.id(),
width,
height,
format,
config,
queue,
&*handler as *const Block<_, _> as *const c_void,
);
CFRelease(config);
stream
};
match unsafe { CGDisplayStreamStart(stream) } {
CGError::Success => Ok(Capturer {
stream,
queue,
width,
height,
format,
display,
stopped,
}),
x => Err(x),
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn format(&self) -> PixelFormat {
self.format
}
pub fn display(&self) -> Display {
self.display
}
}
impl Drop for Capturer {
fn drop(&mut self) {
unsafe {
let _ = CGDisplayStreamStop(self.stream);
loop {
if *self.stopped.lock().unwrap() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(30));
}
CFRelease(self.stream);
dispatch_release(self.queue);
}
}
}

View File

@@ -0,0 +1,75 @@
use std::ptr;
use libc::c_void;
use super::ffi::*;
//TODO: Color space, YCbCr matrix.
pub struct Config {
/// Whether the cursor is visible.
pub cursor: bool,
/// Whether it should letterbox or stretch.
pub letterbox: bool,
/// Minimum seconds per frame.
pub throttle: f64,
/// How many frames are allocated.
/// 3 is the recommended value.
/// 8 is the maximum value.
pub queue_length: i8,
}
impl Config {
/// Don't forget to CFRelease this!
pub fn build(self) -> CFDictionaryRef {
unsafe {
let throttle = CFNumberCreate(
ptr::null_mut(),
CFNumberType::Float64,
&self.throttle as *const _ as *const c_void,
);
let queue_length = CFNumberCreate(
ptr::null_mut(),
CFNumberType::SInt8,
&self.queue_length as *const _ as *const c_void,
);
let keys: [CFStringRef; 4] = [
kCGDisplayStreamShowCursor,
kCGDisplayStreamPreserveAspectRatio,
kCGDisplayStreamMinimumFrameTime,
kCGDisplayStreamQueueDepth,
];
let values: [*mut c_void; 4] = [
cfbool(self.cursor),
cfbool(self.letterbox),
throttle,
queue_length,
];
let res = CFDictionaryCreate(
ptr::null_mut(),
keys.as_ptr(),
values.as_ptr(),
4,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks,
);
CFRelease(throttle);
CFRelease(queue_length);
res
}
}
}
impl Default for Config {
fn default() -> Config {
Config {
cursor: false,
letterbox: true,
throttle: 0.0,
queue_length: 3,
}
}
}

View File

@@ -0,0 +1,63 @@
use std::mem;
use super::ffi::*;
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[repr(C)]
pub struct Display(u32);
impl Display {
pub fn primary() -> Display {
Display(unsafe { CGMainDisplayID() })
}
pub fn online() -> Result<Vec<Display>, CGError> {
unsafe {
let mut arr: [u32; 16] = mem::MaybeUninit::uninit().assume_init();
let mut len: u32 = 0;
match CGGetOnlineDisplayList(16, arr.as_mut_ptr(), &mut len) {
CGError::Success => (),
x => return Err(x),
}
let mut res = Vec::with_capacity(16);
for i in 0..len as usize {
res.push(Display(*arr.get_unchecked(i)));
}
Ok(res)
}
}
pub fn id(self) -> u32 {
self.0
}
pub fn width(self) -> usize {
unsafe { CGDisplayPixelsWide(self.0) }
}
pub fn height(self) -> usize {
unsafe { CGDisplayPixelsHigh(self.0) }
}
pub fn is_builtin(self) -> bool {
unsafe { CGDisplayIsBuiltin(self.0) != 0 }
}
pub fn is_primary(self) -> bool {
unsafe { CGDisplayIsMain(self.0) != 0 }
}
pub fn is_active(self) -> bool {
unsafe { CGDisplayIsActive(self.0) != 0 }
}
pub fn is_online(self) -> bool {
unsafe { CGDisplayIsOnline(self.0) != 0 }
}
pub fn bounds(self) -> CGRect {
unsafe { CGDisplayBounds(self.0) }
}
}

View File

@@ -0,0 +1,240 @@
#![allow(dead_code)]
use block::RcBlock;
use libc::c_void;
pub type CGDisplayStreamRef = *mut c_void;
pub type CFDictionaryRef = *mut c_void;
pub type CFBooleanRef = *mut c_void;
pub type CFNumberRef = *mut c_void;
pub type CFStringRef = *mut c_void;
pub type CGDisplayStreamUpdateRef = *mut c_void;
pub type IOSurfaceRef = *mut c_void;
pub type DispatchQueue = *mut c_void;
pub type DispatchQueueAttr = *mut c_void;
pub type CFAllocatorRef = *mut c_void;
#[repr(C)]
pub struct CFDictionaryKeyCallBacks {
callbacks: [usize; 5],
version: i32,
}
#[repr(C)]
pub struct CFDictionaryValueCallBacks {
callbacks: [usize; 4],
version: i32,
}
macro_rules! pixel_format {
($a:expr, $b:expr, $c:expr, $d:expr) => {
($a as i32) << 24 | ($b as i32) << 16 | ($c as i32) << 8 | ($d as i32)
};
}
pub const SURFACE_LOCK_READ_ONLY: u32 = 0x0000_0001;
pub const SURFACE_LOCK_AVOID_SYNC: u32 = 0x0000_0002;
pub fn cfbool(x: bool) -> CFBooleanRef {
unsafe {
if x {
kCFBooleanTrue
} else {
kCFBooleanFalse
}
}
}
#[repr(i32)]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum CGDisplayStreamFrameStatus {
/// A new frame was generated.
FrameComplete = 0,
/// A new frame was not generated because the display did not change.
FrameIdle = 1,
/// A new frame was not generated because the display has gone blank.
FrameBlank = 2,
/// The display stream was stopped.
Stopped = 3,
#[doc(hidden)]
__Nonexhaustive,
}
#[repr(i32)]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum CFNumberType {
/* Fixed-width types */
SInt8 = 1,
SInt16 = 2,
SInt32 = 3,
SInt64 = 4,
Float32 = 5,
Float64 = 6,
/* 64-bit IEEE 754 */
/* Basic C types */
Char = 7,
Short = 8,
Int = 9,
Long = 10,
LongLong = 11,
Float = 12,
Double = 13,
/* Other */
CFIndex = 14,
NSInteger = 15,
CGFloat = 16,
}
#[repr(i32)]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[must_use]
pub enum CGError {
Success = 0,
Failure = 1000,
IllegalArgument = 1001,
InvalidConnection = 1002,
InvalidContext = 1003,
CannotComplete = 1004,
NotImplemented = 1006,
RangeCheck = 1007,
TypeCheck = 1008,
InvalidOperation = 1010,
NoneAvailable = 1011,
#[doc(hidden)]
__Nonexhaustive,
}
#[repr(i32)]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum PixelFormat {
/// Packed Little Endian ARGB8888
Argb8888 = pixel_format!('B', 'G', 'R', 'A'),
/// Packed Little Endian ARGB2101010
Argb2101010 = pixel_format!('l', '1', '0', 'r'),
/// 2-plane "video" range YCbCr 4:2:0
YCbCr420Video = pixel_format!('4', '2', '0', 'v'),
/// 2-plane "full" range YCbCr 4:2:0
YCbCr420Full = pixel_format!('4', '2', '0', 'f'),
#[doc(hidden)]
__Nonexhaustive,
}
pub type CGDisplayStreamFrameAvailableHandler = *const c_void;
pub type FrameAvailableHandler = RcBlock<
(
CGDisplayStreamFrameStatus, // status
u64, // displayTime
IOSurfaceRef, // frameSurface
CGDisplayStreamUpdateRef, // updateRef
),
(),
>;
#[cfg(target_pointer_width = "64")]
pub type CGFloat = f64;
#[cfg(not(target_pointer_width = "64"))]
pub type CGFloat = f32;
#[repr(C)]
pub struct CGPoint {
pub x: CGFloat,
pub y: CGFloat,
}
#[repr(C)]
pub struct CGSize {
pub width: CGFloat,
pub height: CGFloat,
}
#[repr(C)]
pub struct CGRect {
pub origin: CGPoint,
pub size: CGSize,
}
#[link(name = "System", kind = "dylib")]
#[link(name = "CoreGraphics", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "IOSurface", kind = "framework")]
extern "C" {
// CoreGraphics
pub static kCGDisplayStreamShowCursor: CFStringRef;
pub static kCGDisplayStreamPreserveAspectRatio: CFStringRef;
pub static kCGDisplayStreamMinimumFrameTime: CFStringRef;
pub static kCGDisplayStreamQueueDepth: CFStringRef;
pub fn CGDisplayStreamCreateWithDispatchQueue(
display: u32,
output_width: usize,
output_height: usize,
pixel_format: PixelFormat,
properties: CFDictionaryRef,
queue: DispatchQueue,
handler: CGDisplayStreamFrameAvailableHandler,
) -> CGDisplayStreamRef;
pub fn CGDisplayStreamStart(displayStream: CGDisplayStreamRef) -> CGError;
pub fn CGDisplayStreamStop(displayStream: CGDisplayStreamRef) -> CGError;
pub fn CGMainDisplayID() -> u32;
pub fn CGDisplayPixelsWide(display: u32) -> usize;
pub fn CGDisplayPixelsHigh(display: u32) -> usize;
pub fn CGGetOnlineDisplayList(
max_displays: u32,
online_displays: *mut u32,
display_count: *mut u32,
) -> CGError;
pub fn CGDisplayIsBuiltin(display: u32) -> i32;
pub fn CGDisplayIsMain(display: u32) -> i32;
pub fn CGDisplayIsActive(display: u32) -> i32;
pub fn CGDisplayIsOnline(display: u32) -> i32;
pub fn CGDisplayBounds(display: u32) -> CGRect;
// IOSurface
pub fn IOSurfaceGetAllocSize(buffer: IOSurfaceRef) -> usize;
pub fn IOSurfaceGetBaseAddress(buffer: IOSurfaceRef) -> *mut c_void;
pub fn IOSurfaceIncrementUseCount(buffer: IOSurfaceRef);
pub fn IOSurfaceDecrementUseCount(buffer: IOSurfaceRef);
pub fn IOSurfaceLock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
pub fn IOSurfaceUnlock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
pub fn IOSurfaceGetBaseAddressOfPlane(buffer: IOSurfaceRef, index: usize) -> *mut c_void;
pub fn IOSurfaceGetBytesPerRowOfPlane(buffer: IOSurfaceRef, index: usize) -> usize;
// Dispatch
pub fn dispatch_queue_create(label: *const i8, attr: DispatchQueueAttr) -> DispatchQueue;
pub fn dispatch_release(object: DispatchQueue);
// Core Foundation
pub static kCFTypeDictionaryKeyCallBacks: CFDictionaryKeyCallBacks;
pub static kCFTypeDictionaryValueCallBacks: CFDictionaryValueCallBacks;
// EVEN THE BOOLEANS ARE REFERENCES.
pub static kCFBooleanTrue: CFBooleanRef;
pub static kCFBooleanFalse: CFBooleanRef;
pub fn CFNumberCreate(
allocator: CFAllocatorRef,
theType: CFNumberType,
valuePtr: *const c_void,
) -> CFNumberRef;
pub fn CFDictionaryCreate(
allocator: CFAllocatorRef,
keys: *const *mut c_void,
values: *const *mut c_void,
numValues: i64,
keyCallBacks: *const CFDictionaryKeyCallBacks,
valueCallBacks: *const CFDictionaryValueCallBacks,
) -> CFDictionaryRef;
pub fn CFRetain(cf: *const c_void);
pub fn CFRelease(cf: *const c_void);
}

View File

@@ -0,0 +1,79 @@
use std::{ops, ptr, slice};
use super::ffi::*;
pub struct Frame {
surface: IOSurfaceRef,
inner: &'static [u8],
i420: *mut u8,
i420_len: usize,
}
impl Frame {
pub unsafe fn new(surface: IOSurfaceRef) -> Frame {
CFRetain(surface);
IOSurfaceIncrementUseCount(surface);
IOSurfaceLock(surface, SURFACE_LOCK_READ_ONLY, ptr::null_mut());
let inner = slice::from_raw_parts(
IOSurfaceGetBaseAddress(surface) as *const u8,
IOSurfaceGetAllocSize(surface),
);
Frame {
surface,
inner,
i420: ptr::null_mut(),
i420_len: 0,
}
}
pub fn nv12_to_i420<'a>(&'a mut self, w: usize, h: usize, i420: &'a mut Vec<u8>) {
if self.inner.is_empty() {
return;
}
unsafe {
let plane0 = IOSurfaceGetBaseAddressOfPlane(self.surface, 0);
let stride0 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 0);
let plane1 = IOSurfaceGetBaseAddressOfPlane(self.surface, 1);
let stride1 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 1);
crate::common::nv12_to_i420(
plane0 as _,
stride0 as _,
plane1 as _,
stride1 as _,
w,
h,
i420,
);
self.i420 = i420.as_mut_ptr() as _;
self.i420_len = i420.len();
}
}
}
impl ops::Deref for Frame {
type Target = [u8];
fn deref<'a>(&'a self) -> &'a [u8] {
if self.i420.is_null() {
self.inner
} else {
unsafe {
let inner = slice::from_raw_parts(self.i420 as *const u8, self.i420_len);
inner
}
}
}
}
impl Drop for Frame {
fn drop(&mut self) {
unsafe {
IOSurfaceUnlock(self.surface, SURFACE_LOCK_READ_ONLY, ptr::null_mut());
IOSurfaceDecrementUseCount(self.surface);
CFRelease(self.surface);
}
}
}

View File

@@ -0,0 +1,11 @@
pub use self::capturer::Capturer;
pub use self::config::Config;
pub use self::display::Display;
pub use self::ffi::{CGError, PixelFormat};
pub use self::frame::Frame;
mod capturer;
mod config;
mod display;
pub mod ffi;
mod frame;