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
22fn optional_c_string(value: Option<&str>, parameter_name: &str) -> Result<Option<CString>> {
23 value
24 .map(|text| {
25 CString::new(text).map_err(|_| {
26 CcapError::InvalidParameter(format!("{} contains null byte", parameter_name))
27 })
28 })
29 .transpose()
30}
31
32pub struct Provider {
60 handle: *mut sys::CcapProvider,
61 is_opened: bool,
62 callback_ptr: Option<*mut std::ffi::c_void>,
63}
64
65unsafe impl Send for Provider {}
74
75impl Provider {
76 pub fn new() -> Result<Self> {
78 let handle = unsafe { sys::ccap_provider_create() };
79 if handle.is_null() {
80 return Err(CcapError::DeviceOpenFailed);
81 }
82
83 Ok(Provider {
84 handle,
85 is_opened: false,
86 callback_ptr: None,
87 })
88 }
89
90 pub fn with_device(device_index: i32) -> Result<Self> {
92 Self::with_device_and_extra_info(device_index, None)
93 }
94
95 pub fn with_device_and_extra_info(device_index: i32, extra_info: Option<&str>) -> Result<Self> {
100 let extra_info = optional_c_string(extra_info, "extra info")?;
101 let handle = unsafe {
102 sys::ccap_provider_create_with_index(
103 device_index,
104 extra_info
105 .as_ref()
106 .map_or(ptr::null(), |value| value.as_ptr()),
107 )
108 };
109 if handle.is_null() {
110 return Err(CcapError::InvalidDevice(format!(
111 "device index {}",
112 device_index
113 )));
114 }
115
116 Ok(Provider {
117 handle,
118 is_opened: true,
121 callback_ptr: None,
122 })
123 }
124
125 pub fn with_device_name<S: AsRef<str>>(device_name: S) -> Result<Self> {
127 Self::with_device_name_and_extra_info(device_name, None)
128 }
129
130 pub fn with_device_name_and_extra_info<S: AsRef<str>>(
135 device_name: S,
136 extra_info: Option<&str>,
137 ) -> Result<Self> {
138 let c_name = CString::new(device_name.as_ref()).map_err(|_| {
139 CcapError::InvalidParameter("device name contains null byte".to_string())
140 })?;
141 let extra_info = optional_c_string(extra_info, "extra info")?;
142
143 let handle = unsafe {
144 sys::ccap_provider_create_with_device(
145 c_name.as_ptr(),
146 extra_info
147 .as_ref()
148 .map_or(ptr::null(), |value| value.as_ptr()),
149 )
150 };
151 if handle.is_null() {
152 return Err(CcapError::InvalidDevice(device_name.as_ref().to_string()));
153 }
154
155 Ok(Provider {
156 handle,
157 is_opened: true,
160 callback_ptr: None,
161 })
162 }
163
164 pub fn get_devices() -> Result<Vec<DeviceInfo>> {
166 let provider = Self::new()?;
168 let mut device_names_list = sys::CcapDeviceNamesList::default();
169
170 let success = unsafe {
171 sys::ccap_provider_find_device_names_list(provider.handle, &mut device_names_list)
172 };
173
174 if !success {
175 return Ok(Vec::new());
176 }
177
178 let mut devices = Vec::new();
179 for i in 0..device_names_list.deviceCount {
180 let name_bytes = &device_names_list.deviceNames[i];
181 let name = unsafe {
182 let cstr = CStr::from_ptr(name_bytes.as_ptr());
183 cstr.to_string_lossy().to_string()
184 };
185
186 if let Ok(device_provider) = Self::with_device_name(&name) {
188 if let Ok(device_info) = device_provider.get_device_info_direct() {
189 devices.push(device_info);
190 } else {
191 devices.push(DeviceInfo {
193 name,
194 supported_pixel_formats: Vec::new(),
195 supported_resolutions: Vec::new(),
196 });
197 }
198 }
199 }
200
201 Ok(devices)
202 }
203
204 fn get_device_info_direct(&self) -> Result<DeviceInfo> {
206 let mut device_info = sys::CcapDeviceInfo::default();
207
208 let success = unsafe { sys::ccap_provider_get_device_info(self.handle, &mut device_info) };
209
210 if !success {
211 return Err(CcapError::DeviceOpenFailed);
212 }
213
214 let name = unsafe {
215 let cstr = CStr::from_ptr(device_info.deviceName.as_ptr());
216 cstr.to_string_lossy().to_string()
217 };
218
219 let mut formats = Vec::new();
220 for i in 0..device_info.pixelFormatCount {
221 if i < device_info.supportedPixelFormats.len() {
222 formats.push(PixelFormat::from(device_info.supportedPixelFormats[i]));
223 }
224 }
225
226 let mut resolutions = Vec::new();
227 for i in 0..device_info.resolutionCount {
228 if i < device_info.supportedResolutions.len() {
229 let res = &device_info.supportedResolutions[i];
230 resolutions.push(Resolution {
231 width: res.width,
232 height: res.height,
233 });
234 }
235 }
236
237 Ok(DeviceInfo {
238 name,
239 supported_pixel_formats: formats,
240 supported_resolutions: resolutions,
241 })
242 }
243
244 pub fn open(&mut self) -> Result<()> {
246 if self.is_opened {
247 return Ok(());
248 }
249
250 let result = unsafe { sys::ccap_provider_open_by_index(self.handle, -1, false) };
251 if !result {
252 return Err(CcapError::DeviceOpenFailed);
253 }
254
255 self.is_opened = true;
256 Ok(())
257 }
258
259 pub fn open_device(&mut self, device_name: Option<&str>, auto_start: bool) -> Result<()> {
261 self.open_device_with_extra_info(device_name, None, auto_start)
262 }
263
264 pub fn open_device_with_extra_info(
269 &mut self,
270 device_name: Option<&str>,
271 extra_info: Option<&str>,
272 auto_start: bool,
273 ) -> Result<()> {
274 if let Some(name) = device_name {
275 let c_name = CString::new(name).map_err(|_| {
276 CcapError::InvalidParameter("device name contains null byte".to_string())
277 })?;
278 let extra_info = optional_c_string(extra_info, "extra info")?;
279
280 if !self.handle.is_null() {
282 let _ = self.stop_capture();
285 let _ = self.remove_new_frame_callback();
286 self.cleanup_callback();
287 unsafe {
288 sys::ccap_provider_destroy(self.handle);
289 }
290 self.handle = ptr::null_mut();
291 self.is_opened = false;
292 } else {
293 self.cleanup_callback();
294 }
295
296 self.handle = unsafe {
297 sys::ccap_provider_create_with_device(
298 c_name.as_ptr(),
299 extra_info
300 .as_ref()
301 .map_or(ptr::null(), |value| value.as_ptr()),
302 )
303 };
304 if self.handle.is_null() {
305 return Err(CcapError::InvalidDevice(name.to_string()));
306 }
307 self.is_opened = true;
308 if !auto_start {
309 self.stop_capture()?;
310 }
311 } else if extra_info.is_some() {
312 return self.open_with_index_and_extra_info(-1, extra_info, auto_start);
313 } else {
314 self.open()?;
315 }
316 if auto_start {
317 self.start_capture()?;
318 }
319 Ok(())
320 }
321
322 pub fn device_info(&self) -> Result<DeviceInfo> {
324 self.get_device_info_direct()
325 }
326
327 pub fn is_started(&self) -> bool {
329 unsafe { sys::ccap_provider_is_started(self.handle) }
330 }
331
332 pub fn start(&mut self) -> Result<()> {
334 self.start_capture()
335 }
336
337 pub fn stop(&mut self) -> Result<()> {
339 self.stop_capture()
340 }
341
342 pub fn is_opened(&self) -> bool {
344 self.is_opened
345 }
346
347 pub fn set_property(&mut self, property: PropertyName, value: f64) -> Result<()> {
349 let property_id: sys::CcapPropertyName = property.into();
350 let success = unsafe { sys::ccap_provider_set_property(self.handle, property_id, value) };
351
352 if !success {
353 return Err(CcapError::InvalidParameter(format!(
354 "property {:?}",
355 property
356 )));
357 }
358
359 Ok(())
360 }
361
362 pub fn get_property(&self, property: PropertyName) -> Result<f64> {
364 let property_id: sys::CcapPropertyName = property.into();
365 let value = unsafe { sys::ccap_provider_get_property(self.handle, property_id) };
366
367 Ok(value)
368 }
369
370 pub fn set_resolution(&mut self, width: u32, height: u32) -> Result<()> {
372 let (old_w, old_h) = self.resolution()?;
375
376 self.set_property(PropertyName::Width, width as f64)?;
377 if let Err(e) = self.set_property(PropertyName::Height, height as f64) {
378 let _ = self.set_property(PropertyName::Width, old_w as f64);
380 let _ = self.set_property(PropertyName::Height, old_h as f64);
381 return Err(e);
382 }
383
384 Ok(())
385 }
386
387 pub fn set_frame_rate(&mut self, fps: f64) -> Result<()> {
389 self.set_property(PropertyName::FrameRate, fps)
390 }
391
392 pub fn set_pixel_format(&mut self, format: PixelFormat) -> Result<()> {
394 self.set_property(PropertyName::PixelFormatOutput, format.to_c_enum() as f64)
395 }
396
397 pub fn grab_frame(&mut self, timeout_ms: u32) -> Result<Option<VideoFrame>> {
399 if !self.is_opened {
400 return Err(CcapError::DeviceNotOpened);
401 }
402
403 let frame = unsafe { sys::ccap_provider_grab(self.handle, timeout_ms) };
404 if frame.is_null() {
405 return Ok(None);
406 }
407
408 Ok(Some(VideoFrame::from_c_ptr(frame)))
409 }
410
411 pub fn start_capture(&mut self) -> Result<()> {
413 if !self.is_opened {
414 return Err(CcapError::DeviceNotOpened);
415 }
416
417 let result = unsafe { sys::ccap_provider_start(self.handle) };
418 if !result {
419 return Err(CcapError::CaptureStartFailed);
420 }
421
422 Ok(())
423 }
424
425 pub fn stop_capture(&mut self) -> Result<()> {
427 unsafe { sys::ccap_provider_stop(self.handle) };
428 Ok(())
429 }
430
431 pub fn version() -> Result<String> {
433 let version_ptr = unsafe { sys::ccap_get_version() };
434 if version_ptr.is_null() {
435 return Err(CcapError::Unknown { code: -1 });
436 }
437
438 let version_cstr = unsafe { CStr::from_ptr(version_ptr) };
439 version_cstr
440 .to_str()
441 .map(|s| s.to_string())
442 .map_err(|_| CcapError::Unknown { code: -2 })
443 }
444
445 pub fn list_devices(&self) -> Result<Vec<String>> {
447 let device_infos = Self::get_devices()?;
448 Ok(device_infos.into_iter().map(|info| info.name).collect())
449 }
450
451 pub fn find_device_names(&self) -> Result<Vec<String>> {
453 self.list_devices()
454 }
455
456 pub fn resolution(&self) -> Result<(u32, u32)> {
458 let width = self.get_property(PropertyName::Width)? as u32;
459 let height = self.get_property(PropertyName::Height)? as u32;
460 Ok((width, height))
461 }
462
463 pub fn pixel_format(&self) -> Result<PixelFormat> {
465 let format_val = self.get_property(PropertyName::PixelFormatOutput)? as u32;
466 Ok(PixelFormat::from_c_enum(format_val as sys::CcapPixelFormat))
467 }
468
469 pub fn frame_rate(&self) -> Result<f64> {
471 self.get_property(PropertyName::FrameRate)
472 }
473
474 pub fn set_error_callback<F>(callback: F)
497 where
498 F: Fn(i32, &str) + Send + Sync + 'static,
499 {
500 use std::os::raw::c_char;
501
502 type ErrorCallbackBox = Box<dyn Fn(i32, &str) + Send + Sync>;
503
504 unsafe extern "C" fn error_callback_wrapper(
505 error_code: sys::CcapErrorCode,
506 description: *const c_char,
507 user_data: *mut std::ffi::c_void,
508 ) {
509 if user_data.is_null() || description.is_null() {
510 return;
511 }
512
513 let callback = &**(user_data as *const ErrorCallbackBox);
515 let desc_cstr = std::ffi::CStr::from_ptr(description);
516 if let Ok(desc_str) = desc_cstr.to_str() {
517 callback(error_code as i32, desc_str);
518 }
519 }
520
521 if let Ok(mut guard) = GLOBAL_ERROR_CALLBACK.lock() {
523 if let Some(SendSyncPtr(old_ptr)) = guard.take() {
524 unsafe {
525 let _ = Box::from_raw(old_ptr as *mut ErrorCallbackBox);
526 }
527 }
528
529 let callback_box: ErrorCallbackBox = Box::new(callback);
531 let callback_ptr = Box::into_raw(Box::new(callback_box));
532
533 unsafe {
534 sys::ccap_set_error_callback(
535 Some(error_callback_wrapper),
536 callback_ptr as *mut std::ffi::c_void,
537 );
538 }
539
540 *guard = Some(SendSyncPtr(callback_ptr as *mut std::ffi::c_void));
541 }
542 }
543
544 pub fn set_global_error_callback<F>(callback: F)
548 where
549 F: Fn(i32, &str) + Send + Sync + 'static,
550 {
551 Self::set_error_callback(callback)
552 }
553
554 pub fn clear_error_callback() {
558 type ErrorCallbackBox = Box<dyn Fn(i32, &str) + Send + Sync>;
559
560 if let Ok(mut guard) = GLOBAL_ERROR_CALLBACK.lock() {
562 unsafe {
564 sys::ccap_set_error_callback(None, ptr::null_mut());
565 }
566 if let Some(SendSyncPtr(old_ptr)) = guard.take() {
567 unsafe {
568 let _ = Box::from_raw(old_ptr as *mut ErrorCallbackBox);
569 }
570 }
571 }
572 }
573
574 pub fn clear_global_error_callback() {
578 Self::clear_error_callback()
579 }
580
581 pub fn open_with_index(&mut self, device_index: i32, auto_start: bool) -> Result<()> {
583 self.open_with_index_and_extra_info(device_index, None, auto_start)
584 }
585
586 pub fn open_with_index_and_extra_info(
591 &mut self,
592 device_index: i32,
593 extra_info: Option<&str>,
594 auto_start: bool,
595 ) -> Result<()> {
596 let extra_info = optional_c_string(extra_info, "extra info")?;
597
598 if !self.handle.is_null() {
601 let _ = self.stop_capture();
602 let _ = self.remove_new_frame_callback();
603 self.cleanup_callback();
604 unsafe {
605 sys::ccap_provider_destroy(self.handle);
606 }
607 self.handle = ptr::null_mut();
608 self.is_opened = false;
609 } else {
610 self.cleanup_callback();
612 }
613
614 self.handle = unsafe {
616 sys::ccap_provider_create_with_index(
617 device_index,
618 extra_info
619 .as_ref()
620 .map_or(ptr::null(), |value| value.as_ptr()),
621 )
622 };
623
624 if self.handle.is_null() {
625 return Err(CcapError::InvalidDevice(format!(
626 "device index {}",
627 device_index
628 )));
629 }
630
631 self.is_opened = true;
633 if !auto_start {
634 self.stop_capture()?;
635 }
636 if auto_start {
637 self.start_capture()?;
638 }
639 Ok(())
640 }
641
642 pub fn set_new_frame_callback<F>(&mut self, callback: F) -> Result<()>
661 where
662 F: Fn(&VideoFrame) -> bool + Send + Sync + 'static,
663 {
664 use std::os::raw::c_void;
665
666 type CallbackBox = Box<dyn Fn(&VideoFrame) -> bool + Send + Sync>;
668
669 self.cleanup_callback();
671
672 unsafe extern "C" fn new_frame_callback_wrapper(
673 frame: *const sys::CcapVideoFrame,
674 user_data: *mut c_void,
675 ) -> bool {
676 if user_data.is_null() || frame.is_null() {
677 return false;
678 }
679
680 let callback = &**(user_data as *const CallbackBox);
682
683 let video_frame = VideoFrame::from_c_ptr_ref(frame as *mut sys::CcapVideoFrame);
685 callback(&video_frame)
686 }
687
688 let callback_box: CallbackBox = Box::new(callback);
691 let callback_ptr = Box::into_raw(Box::new(callback_box));
692
693 let success = unsafe {
694 sys::ccap_provider_set_new_frame_callback(
695 self.handle,
696 Some(new_frame_callback_wrapper),
697 callback_ptr as *mut c_void,
698 )
699 };
700
701 if success {
702 self.callback_ptr = Some(callback_ptr as *mut c_void);
703 Ok(())
704 } else {
705 unsafe {
707 let _ = Box::from_raw(callback_ptr);
708 }
709 Err(CcapError::InvalidParameter(
710 "Failed to set frame callback".to_string(),
711 ))
712 }
713 }
714
715 pub fn remove_new_frame_callback(&mut self) -> Result<()> {
717 let success = unsafe {
718 sys::ccap_provider_set_new_frame_callback(self.handle, None, ptr::null_mut())
719 };
720
721 if success {
722 self.cleanup_callback();
723 Ok(())
724 } else {
725 Err(CcapError::CaptureStopFailed)
726 }
727 }
728
729 fn cleanup_callback(&mut self) {
731 type CallbackBox = Box<dyn Fn(&VideoFrame) -> bool + Send + Sync>;
733
734 if let Some(callback_ptr) = self.callback_ptr.take() {
735 unsafe {
736 let _ = Box::from_raw(callback_ptr as *mut CallbackBox);
739 }
740 }
741 }
742}
743
744impl Drop for Provider {
745 fn drop(&mut self) {
746 self.cleanup_callback();
748
749 if !self.handle.is_null() {
750 unsafe {
751 sys::ccap_provider_destroy(self.handle);
752 }
753 self.handle = ptr::null_mut();
754 }
755 }
756}