use av_foundation::{ capture_device::{ AVCaptureDevice, AVCaptureDeviceDiscoverySession, AVCaptureDevicePositionUnspecified, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeExternalUnknown, }, capture_input::AVCaptureDeviceInput, capture_output_base::AVCaptureOutput, capture_session::{AVCaptureConnection, AVCaptureSession}, capture_video_data_output::{AVCaptureVideoDataOutput, AVCaptureVideoDataOutputSampleBufferDelegate}, media_format::AVMediaTypeVideo, }; use core_foundation::base::TCFType; use core_media::sample_buffer::{CMSampleBuffer, CMSampleBufferRef}; use core_video::pixel_buffer::CVPixelBuffer; use dispatch2::Queue; use objc2::{ declare_class, extern_methods, msg_send_id, mutability, rc::{Allocated, Id}, runtime::ProtocolObject, ClassType, DeclaredClass, }; use objc2_foundation::{NSMutableArray, NSObject, NSObjectProtocol}; use os_ver::if_greater_than; pub struct DelegateIvars {} declare_class!( struct Delegate; unsafe impl ClassType for Delegate { type Super = NSObject; type Mutability = mutability::Mutable; const NAME: &'static str = "OutputSampleBufferDelegate"; } impl DeclaredClass for Delegate { type Ivars = DelegateIvars; } unsafe impl NSObjectProtocol for Delegate {} unsafe impl AVCaptureVideoDataOutputSampleBufferDelegate for Delegate { #[method(captureOutput:didOutputSampleBuffer:fromConnection:)] unsafe fn capture_output_did_output_sample_buffer( &self, _capture_output: &AVCaptureOutput, sample_buffer: CMSampleBufferRef, _connection: &AVCaptureConnection, ) { let sample_buffer = CMSampleBuffer::wrap_under_get_rule(sample_buffer); if let Some(image_buffer) = sample_buffer.get_image_buffer() { if let Some(pixel_buffer) = image_buffer.downcast::() { println!("pixel buffer: {:?}", pixel_buffer); } } } } unsafe impl Delegate { #[method_id(init)] fn init(this: Allocated) -> Option> { let this = this.set_ivars(DelegateIvars {}); unsafe { msg_send_id![super(this), init] } } } ); extern_methods!( unsafe impl Delegate { #[method_id(new)] pub fn new() -> Id; } ); fn main() { let devices = unsafe { if cfg!(target_os = "macos") { if_greater_than! {(10, 15) => { let mut device_types = NSMutableArray::new(); device_types.addObject(AVCaptureDeviceTypeBuiltInWideAngleCamera); if_greater_than!{(14) => { device_types.addObject(AVCaptureDeviceTypeExternal); } else { device_types.addObject(AVCaptureDeviceTypeExternalUnknown); }}; device_types.addObject(AVCaptureDeviceTypeExternalUnknown); AVCaptureDeviceDiscoverySession::discovery_session_with_device_types( &device_types, AVMediaTypeVideo, AVCaptureDevicePositionUnspecified, ).devices() } else { AVCaptureDevice::devices_with_media_type(AVMediaTypeVideo) }} } else if cfg!(target_os = "ios") { if_greater_than! {(10) => { let mut device_types = NSMutableArray::new(); device_types.addObject(AVCaptureDeviceTypeBuiltInWideAngleCamera); AVCaptureDeviceDiscoverySession::discovery_session_with_device_types( &device_types, AVMediaTypeVideo, AVCaptureDevicePositionUnspecified, ).devices() } else { AVCaptureDevice::devices_with_media_type(AVMediaTypeVideo) }} } else { println!("Unsupported platform"); return; } }; for device in devices.iter() { println!("name: {:?}", device.localized_name()); println!("id: {:?}", device.unique_id()); println!("model: {:?}", device.model_id()); println!("manufacturer: {:?}", device.manufacturer()); println!("device type: {:?}", device.device_type()); println!("transport type: {:?}", String::from_utf8_lossy(&device.transport_type().to_be_bytes())); println!("position: {:?}", device.position()); } let device = match devices.first() { Some(device) => device, None => { println!("No video capture device found"); return; } }; let session = AVCaptureSession::new(); let input = AVCaptureDeviceInput::from_device(device).unwrap(); let output = AVCaptureVideoDataOutput::new(); let delegate = Delegate::new(); let delegate = ProtocolObject::from_ref(&*delegate); let queue = Queue::new("com.video_capture.queue", dispatch2::QueueAttribute::Serial); output.set_sample_buffer_delegate(delegate, &queue); output.set_always_discards_late_video_frames(true); session.begin_configuration(); session.add_input(&input); session.add_output(&output); session.commit_configuration(); session.start_running(); std::thread::sleep(std::time::Duration::from_secs(10)); session.stop_running(); }