#![allow(dead_code)]
use libc::c_void;
use objc2::declare::ClassBuilder;
use objc2::rc::Retained;
use objc2::runtime::{AnyProtocol, Bool, Sel};
use objc2::{sel, ClassType};
use objc2_foundation::NSObject;
use std::ffi::CStr;
use std::ptr;
pub type DelegateCallback =
extern "C" fn(*mut c_void, Sel, *mut c_void, *mut c_void, *mut c_void);
#[link(name = "System")]
extern "C" {
fn dispatch_queue_create(label: *const i8, attr: *const c_void) -> *mut c_void;
}
#[link(name = "objc", kind = "dylib")]
extern "C" {
fn class_addMethod(
cls: *const c_void,
name: Sel,
imp: *const c_void,
types: *const i8,
) -> Bool;
}
pub fn create_capture_delegate(
class_name: &str,
protocol_name: &str,
callback: DelegateCallback,
) -> Result<Retained<NSObject>, &'static str> {
let class_name_cstr = format!("{}\0", class_name);
let protocol_name_cstr = format!("{}\0", protocol_name);
let class_name = CStr::from_bytes_with_nul(class_name_cstr.as_bytes())
.map_err(|_| "Invalid class name")?;
let protocol_name = CStr::from_bytes_with_nul(protocol_name_cstr.as_bytes())
.map_err(|_| "Invalid protocol name")?;
create_capture_delegate_cstr(class_name, protocol_name, callback)
}
pub fn create_capture_delegate_cstr(
class_name: &CStr,
protocol_name: &CStr,
callback: DelegateCallback,
) -> Result<Retained<NSObject>, &'static str> {
let protocol = AnyProtocol::get(protocol_name).ok_or("Protocol not found")?;
let mut builder =
ClassBuilder::new(class_name, NSObject::class()).ok_or("Failed to create class builder")?;
builder.add_protocol(protocol);
let delegate_class = builder.register();
unsafe {
let method_sel = sel!(captureOutput:didOutputSampleBuffer:fromConnection:);
let method_types = b"v@:@@@\0";
let added = class_addMethod(
delegate_class as *const _ as *const c_void,
method_sel,
callback as *const c_void,
method_types.as_ptr() as *const i8,
);
if !added.as_bool() {
return Err("Failed to add method to delegate class");
}
let delegate: Retained<NSObject> = objc2::msg_send![delegate_class, new];
Ok(delegate)
}
}
pub fn create_dispatch_queue(label: &str) -> *mut c_void {
let label_cstr = format!("{}\0", label);
unsafe { dispatch_queue_create(label_cstr.as_ptr() as *const i8, ptr::null()) }
}
pub unsafe fn set_sample_buffer_delegate(
output: *const c_void,
delegate: *const c_void,
queue: *const c_void,
) {
#[link(name = "objc", kind = "dylib")]
extern "C" {
#[link_name = "objc_msgSend"]
fn objc_msgSend_set_delegate(
receiver: *const c_void,
sel: Sel,
delegate: *const c_void,
queue: *const c_void,
);
}
let set_delegate_sel = sel!(setSampleBufferDelegate:queue:);
objc_msgSend_set_delegate(output, set_delegate_sel, delegate, queue);
}
pub struct CaptureDelegate {
delegate: Retained<NSObject>,
queue: *mut c_void,
}
impl CaptureDelegate {
pub fn new_video(class_name: &str, callback: DelegateCallback) -> Result<Self, &'static str> {
let delegate = create_capture_delegate(
class_name,
"AVCaptureVideoDataOutputSampleBufferDelegate",
callback,
)?;
let queue_label = format!("com.videotoolbox.{}.queue", class_name);
let queue = create_dispatch_queue(&queue_label);
Ok(Self { delegate, queue })
}
pub fn new_audio(class_name: &str, callback: DelegateCallback) -> Result<Self, &'static str> {
let delegate = create_capture_delegate(
class_name,
"AVCaptureAudioDataOutputSampleBufferDelegate",
callback,
)?;
let queue_label = format!("com.videotoolbox.{}.queue", class_name);
let queue = create_dispatch_queue(&queue_label);
Ok(Self { delegate, queue })
}
pub fn delegate(&self) -> &Retained<NSObject> {
&self.delegate
}
pub fn queue(&self) -> *mut c_void {
self.queue
}
pub unsafe fn attach_to(&self, output: *const c_void) {
set_sample_buffer_delegate(
output,
&*self.delegate as *const _ as *const c_void,
self.queue as *const c_void,
);
}
}
unsafe impl Send for CaptureDelegate {}