1use crate::{error::*, frame::*, sys, types::*};
4use std::ffi::{CStr, CString};
5use std::ptr;
6use std::sync::Mutex;
7
8struct SendSyncPtr(*mut std::ffi::c_void);
12
13unsafe impl Send for SendSyncPtr {}
17unsafe impl Sync for SendSyncPtr {}
18
19static GLOBAL_ERROR_CALLBACK: Mutex<Option<SendSyncPtr>> = Mutex::new(None);
21
22pub struct Provider {
50 handle: *mut sys::CcapProvider,
51 is_opened: bool,
52 callback_ptr: Option<*mut std::ffi::c_void>,
53}
54
55unsafe impl Send for Provider {}
64
65impl Provider {
66 pub fn new() -> Result<Self> {
68 let handle = unsafe { sys::ccap_provider_create() };
69 if handle.is_null() {
70 return Err(CcapError::DeviceOpenFailed);
71 }
72
73 Ok(Provider {
74 handle,
75 is_opened: false,
76 callback_ptr: None,
77 })
78 }
79
80 pub fn with_device(device_index: i32) -> Result<Self> {
82 let handle = unsafe { sys::ccap_provider_create_with_index(device_index, ptr::null()) };
83 if handle.is_null() {
84 return Err(CcapError::InvalidDevice(format!(
85 "device index {}",
86 device_index
87 )));
88 }
89
90 Ok(Provider {
91 handle,
92 is_opened: true,
95 callback_ptr: None,
96 })
97 }
98
99 pub fn with_device_name<S: AsRef<str>>(device_name: S) -> Result<Self> {
101 let c_name = CString::new(device_name.as_ref()).map_err(|_| {
102 CcapError::InvalidParameter("device name contains null byte".to_string())
103 })?;
104
105 let handle = unsafe { sys::ccap_provider_create_with_device(c_name.as_ptr(), ptr::null()) };
106 if handle.is_null() {
107 return Err(CcapError::InvalidDevice(device_name.as_ref().to_string()));
108 }
109
110 Ok(Provider {
111 handle,
112 is_opened: true,
115 callback_ptr: None,
116 })
117 }
118
119 pub fn get_devices() -> Result<Vec<DeviceInfo>> {
121 let provider = Self::new()?;
123 let mut device_names_list = sys::CcapDeviceNamesList::default();
124
125 let success = unsafe {
126 sys::ccap_provider_find_device_names_list(provider.handle, &mut device_names_list)
127 };
128
129 if !success {
130 return Ok(Vec::new());
131 }
132
133 let mut devices = Vec::new();
134 for i in 0..device_names_list.deviceCount {
135 let name_bytes = &device_names_list.deviceNames[i];
136 let name = unsafe {
137 let cstr = CStr::from_ptr(name_bytes.as_ptr());
138 cstr.to_string_lossy().to_string()
139 };
140
141 if let Ok(device_provider) = Self::with_device_name(&name) {
143 if let Ok(device_info) = device_provider.get_device_info_direct() {
144 devices.push(device_info);
145 } else {
146 devices.push(DeviceInfo {
148 name,
149 supported_pixel_formats: Vec::new(),
150 supported_resolutions: Vec::new(),
151 });
152 }
153 }
154 }
155
156 Ok(devices)
157 }
158
159 fn get_device_info_direct(&self) -> Result<DeviceInfo> {
161 let mut device_info = sys::CcapDeviceInfo::default();
162
163 let success = unsafe { sys::ccap_provider_get_device_info(self.handle, &mut device_info) };
164
165 if !success {
166 return Err(CcapError::DeviceOpenFailed);
167 }
168
169 let name = unsafe {
170 let cstr = CStr::from_ptr(device_info.deviceName.as_ptr());
171 cstr.to_string_lossy().to_string()
172 };
173
174 let mut formats = Vec::new();
175 for i in 0..device_info.pixelFormatCount {
176 if i < device_info.supportedPixelFormats.len() {
177 formats.push(PixelFormat::from(device_info.supportedPixelFormats[i]));
178 }
179 }
180
181 let mut resolutions = Vec::new();
182 for i in 0..device_info.resolutionCount {
183 if i < device_info.supportedResolutions.len() {
184 let res = &device_info.supportedResolutions[i];
185 resolutions.push(Resolution {
186 width: res.width,
187 height: res.height,
188 });
189 }
190 }
191
192 Ok(DeviceInfo {
193 name,
194 supported_pixel_formats: formats,
195 supported_resolutions: resolutions,
196 })
197 }
198
199 pub fn open(&mut self) -> Result<()> {
201 if self.is_opened {
202 return Ok(());
203 }
204
205 let result = unsafe { sys::ccap_provider_open_by_index(self.handle, -1, false) };
206 if !result {
207 return Err(CcapError::DeviceOpenFailed);
208 }
209
210 self.is_opened = true;
211 Ok(())
212 }
213
214 pub fn open_device(&mut self, device_name: Option<&str>, auto_start: bool) -> Result<()> {
216 if let Some(name) = device_name {
217 if !self.handle.is_null() {
219 let _ = self.stop_capture();
222 let _ = self.remove_new_frame_callback();
223 self.cleanup_callback();
224 unsafe {
225 sys::ccap_provider_destroy(self.handle);
226 }
227 }
228 let c_name = CString::new(name).map_err(|_| {
229 CcapError::InvalidParameter("device name contains null byte".to_string())
230 })?;
231 self.handle =
232 unsafe { sys::ccap_provider_create_with_device(c_name.as_ptr(), ptr::null()) };
233 if self.handle.is_null() {
234 return Err(CcapError::InvalidDevice(name.to_string()));
235 }
236 self.is_opened = true;
237 } else {
238 self.open()?;
239 }
240 if auto_start {
241 self.start_capture()?;
242 }
243 Ok(())
244 }
245
246 pub fn device_info(&self) -> Result<DeviceInfo> {
248 self.get_device_info_direct()
249 }
250
251 pub fn is_started(&self) -> bool {
253 unsafe { sys::ccap_provider_is_started(self.handle) }
254 }
255
256 pub fn start(&mut self) -> Result<()> {
258 self.start_capture()
259 }
260
261 pub fn stop(&mut self) -> Result<()> {
263 self.stop_capture()
264 }
265
266 pub fn is_opened(&self) -> bool {
268 self.is_opened
269 }
270
271 pub fn set_property(&mut self, property: PropertyName, value: f64) -> Result<()> {
273 let property_id: sys::CcapPropertyName = property.into();
274 let success = unsafe { sys::ccap_provider_set_property(self.handle, property_id, value) };
275
276 if !success {
277 return Err(CcapError::InvalidParameter(format!(
278 "property {:?}",
279 property
280 )));
281 }
282
283 Ok(())
284 }
285
286 pub fn get_property(&self, property: PropertyName) -> Result<f64> {
288 let property_id: sys::CcapPropertyName = property.into();
289 let value = unsafe { sys::ccap_provider_get_property(self.handle, property_id) };
290
291 Ok(value)
292 }
293
294 pub fn set_resolution(&mut self, width: u32, height: u32) -> Result<()> {
296 let (old_w, old_h) = self.resolution()?;
299
300 self.set_property(PropertyName::Width, width as f64)?;
301 if let Err(e) = self.set_property(PropertyName::Height, height as f64) {
302 let _ = self.set_property(PropertyName::Width, old_w as f64);
304 let _ = self.set_property(PropertyName::Height, old_h as f64);
305 return Err(e);
306 }
307
308 Ok(())
309 }
310
311 pub fn set_frame_rate(&mut self, fps: f64) -> Result<()> {
313 self.set_property(PropertyName::FrameRate, fps)
314 }
315
316 pub fn set_pixel_format(&mut self, format: PixelFormat) -> Result<()> {
318 self.set_property(PropertyName::PixelFormatOutput, format.to_c_enum() as f64)
319 }
320
321 pub fn grab_frame(&mut self, timeout_ms: u32) -> Result<Option<VideoFrame>> {
323 if !self.is_opened {
324 return Err(CcapError::DeviceNotOpened);
325 }
326
327 let frame = unsafe { sys::ccap_provider_grab(self.handle, timeout_ms) };
328 if frame.is_null() {
329 return Ok(None);
330 }
331
332 Ok(Some(VideoFrame::from_c_ptr(frame)))
333 }
334
335 pub fn start_capture(&mut self) -> Result<()> {
337 if !self.is_opened {
338 return Err(CcapError::DeviceNotOpened);
339 }
340
341 let result = unsafe { sys::ccap_provider_start(self.handle) };
342 if !result {
343 return Err(CcapError::CaptureStartFailed);
344 }
345
346 Ok(())
347 }
348
349 pub fn stop_capture(&mut self) -> Result<()> {
351 unsafe { sys::ccap_provider_stop(self.handle) };
352 Ok(())
353 }
354
355 pub fn version() -> Result<String> {
357 let version_ptr = unsafe { sys::ccap_get_version() };
358 if version_ptr.is_null() {
359 return Err(CcapError::Unknown { code: -1 });
360 }
361
362 let version_cstr = unsafe { CStr::from_ptr(version_ptr) };
363 version_cstr
364 .to_str()
365 .map(|s| s.to_string())
366 .map_err(|_| CcapError::Unknown { code: -2 })
367 }
368
369 pub fn list_devices(&self) -> Result<Vec<String>> {
371 let device_infos = Self::get_devices()?;
372 Ok(device_infos.into_iter().map(|info| info.name).collect())
373 }
374
375 pub fn find_device_names(&self) -> Result<Vec<String>> {
377 self.list_devices()
378 }
379
380 pub fn resolution(&self) -> Result<(u32, u32)> {
382 let width = self.get_property(PropertyName::Width)? as u32;
383 let height = self.get_property(PropertyName::Height)? as u32;
384 Ok((width, height))
385 }
386
387 pub fn pixel_format(&self) -> Result<PixelFormat> {
389 let format_val = self.get_property(PropertyName::PixelFormatOutput)? as u32;
390 Ok(PixelFormat::from_c_enum(format_val as sys::CcapPixelFormat))
391 }
392
393 pub fn frame_rate(&self) -> Result<f64> {
395 self.get_property(PropertyName::FrameRate)
396 }
397
398 pub fn set_error_callback<F>(callback: F)
421 where
422 F: Fn(i32, &str) + Send + Sync + 'static,
423 {
424 use std::os::raw::c_char;
425
426 type ErrorCallbackBox = Box<dyn Fn(i32, &str) + Send + Sync>;
427
428 unsafe extern "C" fn error_callback_wrapper(
429 error_code: sys::CcapErrorCode,
430 description: *const c_char,
431 user_data: *mut std::ffi::c_void,
432 ) {
433 if user_data.is_null() || description.is_null() {
434 return;
435 }
436
437 let callback = &**(user_data as *const ErrorCallbackBox);
439 let desc_cstr = std::ffi::CStr::from_ptr(description);
440 if let Ok(desc_str) = desc_cstr.to_str() {
441 callback(error_code as i32, desc_str);
442 }
443 }
444
445 if let Ok(mut guard) = GLOBAL_ERROR_CALLBACK.lock() {
447 if let Some(SendSyncPtr(old_ptr)) = guard.take() {
448 unsafe {
449 let _ = Box::from_raw(old_ptr as *mut ErrorCallbackBox);
450 }
451 }
452
453 let callback_box: ErrorCallbackBox = Box::new(callback);
455 let callback_ptr = Box::into_raw(Box::new(callback_box));
456
457 unsafe {
458 sys::ccap_set_error_callback(
459 Some(error_callback_wrapper),
460 callback_ptr as *mut std::ffi::c_void,
461 );
462 }
463
464 *guard = Some(SendSyncPtr(callback_ptr as *mut std::ffi::c_void));
465 }
466 }
467
468 pub fn set_global_error_callback<F>(callback: F)
472 where
473 F: Fn(i32, &str) + Send + Sync + 'static,
474 {
475 Self::set_error_callback(callback)
476 }
477
478 pub fn clear_error_callback() {
482 type ErrorCallbackBox = Box<dyn Fn(i32, &str) + Send + Sync>;
483
484 if let Ok(mut guard) = GLOBAL_ERROR_CALLBACK.lock() {
486 unsafe {
488 sys::ccap_set_error_callback(None, ptr::null_mut());
489 }
490 if let Some(SendSyncPtr(old_ptr)) = guard.take() {
491 unsafe {
492 let _ = Box::from_raw(old_ptr as *mut ErrorCallbackBox);
493 }
494 }
495 }
496 }
497
498 pub fn clear_global_error_callback() {
502 Self::clear_error_callback()
503 }
504
505 pub fn open_with_index(&mut self, device_index: i32, auto_start: bool) -> Result<()> {
507 if !self.handle.is_null() {
510 let _ = self.stop_capture();
511 let _ = self.remove_new_frame_callback();
512 self.cleanup_callback();
513 unsafe {
514 sys::ccap_provider_destroy(self.handle);
515 }
516 } else {
517 self.cleanup_callback();
519 }
520
521 self.handle = unsafe { sys::ccap_provider_create_with_index(device_index, ptr::null()) };
523
524 if self.handle.is_null() {
525 return Err(CcapError::InvalidDevice(format!(
526 "device index {}",
527 device_index
528 )));
529 }
530
531 self.is_opened = true;
533 if auto_start {
534 self.start_capture()?;
535 }
536 Ok(())
537 }
538
539 pub fn set_new_frame_callback<F>(&mut self, callback: F) -> Result<()>
558 where
559 F: Fn(&VideoFrame) -> bool + Send + Sync + 'static,
560 {
561 use std::os::raw::c_void;
562
563 type CallbackBox = Box<dyn Fn(&VideoFrame) -> bool + Send + Sync>;
565
566 self.cleanup_callback();
568
569 unsafe extern "C" fn new_frame_callback_wrapper(
570 frame: *const sys::CcapVideoFrame,
571 user_data: *mut c_void,
572 ) -> bool {
573 if user_data.is_null() || frame.is_null() {
574 return false;
575 }
576
577 let callback = &**(user_data as *const CallbackBox);
579
580 let video_frame = VideoFrame::from_c_ptr_ref(frame as *mut sys::CcapVideoFrame);
582 callback(&video_frame)
583 }
584
585 let callback_box: CallbackBox = Box::new(callback);
588 let callback_ptr = Box::into_raw(Box::new(callback_box));
589
590 let success = unsafe {
591 sys::ccap_provider_set_new_frame_callback(
592 self.handle,
593 Some(new_frame_callback_wrapper),
594 callback_ptr as *mut c_void,
595 )
596 };
597
598 if success {
599 self.callback_ptr = Some(callback_ptr as *mut c_void);
600 Ok(())
601 } else {
602 unsafe {
604 let _ = Box::from_raw(callback_ptr);
605 }
606 Err(CcapError::InvalidParameter(
607 "Failed to set frame callback".to_string(),
608 ))
609 }
610 }
611
612 pub fn remove_new_frame_callback(&mut self) -> Result<()> {
614 let success = unsafe {
615 sys::ccap_provider_set_new_frame_callback(self.handle, None, ptr::null_mut())
616 };
617
618 if success {
619 self.cleanup_callback();
620 Ok(())
621 } else {
622 Err(CcapError::CaptureStopFailed)
623 }
624 }
625
626 fn cleanup_callback(&mut self) {
628 type CallbackBox = Box<dyn Fn(&VideoFrame) -> bool + Send + Sync>;
630
631 if let Some(callback_ptr) = self.callback_ptr.take() {
632 unsafe {
633 let _ = Box::from_raw(callback_ptr as *mut CallbackBox);
636 }
637 }
638 }
639}
640
641impl Drop for Provider {
642 fn drop(&mut self) {
643 self.cleanup_callback();
645
646 if !self.handle.is_null() {
647 unsafe {
648 sys::ccap_provider_destroy(self.handle);
649 }
650 self.handle = ptr::null_mut();
651 }
652 }
653}