1use std::{convert::TryInto, pin::Pin};
5
6use machine_vision_formats as formats;
7
8use vmbc_sys::{
9 VmbCameraInfo_t, VmbErrorType, VmbFeaturePersistSettings_t, VmbFrame_t, VmbFrameCallback,
10 VmbFrameStatusType, VmbHandle_t, VmbVersionInfo_t,
11};
12
13fn err_str(err: i32) -> &'static str {
14 use VmbErrorType::*;
15 #[expect(non_upper_case_globals)]
16 match err {
17 VmbErrorSuccess => "VmbErrorSuccess",
18 VmbErrorInternalFault => "VmbErrorInternalFault",
19 VmbErrorApiNotStarted => "VmbErrorApiNotStarted",
20 VmbErrorNotFound => "VmbErrorNotFound",
21 VmbErrorBadHandle => "VmbErrorBadHandle",
22 VmbErrorDeviceNotOpen => "VmbErrorDeviceNotOpen",
23 VmbErrorInvalidAccess => "VmbErrorInvalidAccess",
24 VmbErrorBadParameter => "VmbErrorBadParameter",
25 VmbErrorStructSize => "VmbErrorStructSize",
26 VmbErrorMoreData => "VmbErrorMoreData",
27 VmbErrorWrongType => "VmbErrorWrongType",
28 VmbErrorInvalidValue => "VmbErrorInvalidValue",
29 VmbErrorTimeout => "VmbErrorTimeout",
30 VmbErrorOther => "VmbErrorOther",
31 VmbErrorResources => "VmbErrorResources",
32 VmbErrorInvalidCall => "VmbErrorInvalidCall",
33 VmbErrorNoTL => "VmbErrorNoTL",
34 VmbErrorNotImplemented => "VmbErrorNotImplemented",
35 VmbErrorNotSupported => "VmbErrorNotSupported",
36 VmbErrorIncomplete => "VmbErrorIncomplete",
37 VmbErrorIO => "VmbErrorIO",
38 VmbErrorValidValueSetNotPresent => "VmbErrorValidValueSetNotPresent",
39 VmbErrorGenTLUnspecified => "VmbErrorGenTLUnspecified",
40 VmbErrorUnspecified => "VmbErrorUnspecified",
41 VmbErrorBusy => "VmbErrorBusy",
42 VmbErrorNoData => "VmbErrorNoData",
43 VmbErrorParsingChunkData => "VmbErrorParsingChunkData",
44 VmbErrorInUse => "VmbErrorInUse",
45 VmbErrorUnknown => "VmbErrorUnknown",
46 VmbErrorXml => "VmbErrorXml",
47 VmbErrorNotAvailable => "VmbErrorNotAvailable",
48 VmbErrorNotInitialized => "VmbErrorNotInitialized",
49 VmbErrorInvalidAddress => "VmbErrorInvalidAddress",
50 VmbErrorAlready => "VmbErrorAlready",
51 VmbErrorNoChunkData => "VmbErrorNoChunkData",
52 VmbErrorUserCallbackException => "VmbErrorUserCallbackException",
53 VmbErrorFeaturesUnavailable => "VmbErrorFeaturesUnavailable",
54 VmbErrorTLNotFound => "VmbErrorTLNotFound",
55 VmbErrorAmbiguous => "VmbErrorAmbiguous",
56 VmbErrorRetriesExceeded => "VmbErrorRetriesExceeded",
57 VmbErrorInsufficientBufferCount => "VmbErrorInsufficientBufferCount",
58 VmbErrorCustom => "VmbErrorCustom",
59 _ => "unknown error",
60 }
61}
62
63#[derive(thiserror::Error, Debug)]
64#[error("Vimba X error {code}: {msg}")]
65pub struct VimbaError {
66 pub code: i32,
67 pub msg: &'static str,
68}
69
70impl From<i32> for VimbaError {
71 fn from(code: i32) -> VimbaError {
72 VimbaError {
73 code,
74 msg: err_str(code),
75 }
76 }
77}
78
79#[derive(thiserror::Error, Debug)]
80pub enum Error {
81 #[error("Loading library at {vimbac_path}")]
82 LibLoading {
83 source: libloading::Error,
84 vimbac_path: std::path::PathBuf,
85 },
86 #[error("{source}")]
87 Vimba {
88 #[from]
89 source: VimbaError,
90 },
91 #[error("{source}")]
92 NulError {
93 #[from]
94 source: std::ffi::NulError,
95 },
96 #[error("{source}")]
97 Utf8Error {
98 #[from]
99 source: std::str::Utf8Error,
100 },
101 #[error("unknown pixel format {fmt}")]
102 UnknownPixelFormat { fmt: String },
103 #[error("unknown pixel format code 0x{code:X}")]
104 UnknownPixelFormatCode { code: u32 },
105 #[error("invalid call")]
106 InvalidCall {},
107 #[cfg(target_os = "windows")]
108 #[error("{source}")]
109 Windows {
110 #[from]
111 source: windows::core::Error,
112 },
113}
114
115pub type Result<T> = std::result::Result<T, Error>;
116
117fn vimba_err(err: i32) -> std::result::Result<(), VimbaError> {
118 if err == VmbErrorType::VmbErrorSuccess {
119 Ok(())
120 } else {
121 Err(VimbaError::from(err))
122 }
123}
124
125macro_rules! vimba_call_no_err {
126 ($expr: expr_2021) => {{
127 tracing::debug!("calling: {} {}:{}", stringify!($expr), file!(), line!());
128 unsafe { $expr }
129 }};
130}
131
132macro_rules! vimba_call {
133 ($expr: expr_2021) => {{
134 let errcode = vimba_call_no_err!($expr);
135 tracing::debug!(" errcode: {}", errcode);
136
137 vimba_err(errcode)
138 }};
139}
140
141pub struct VimbaLibrary {
142 pub vimba_lib: vmbc_sys::VimbaC,
143 started: bool,
144}
145
146impl VimbaLibrary {
147 pub fn new() -> std::result::Result<Self, Error> {
148 let vimbac_path = match std::env::var_os("VIMBA_X_HOME") {
149 Some(vimba_x_home) => std::path::PathBuf::from(vimba_x_home)
150 .join("api")
151 .join("lib"),
152 None => {
153 #[cfg(target_os = "windows")]
154 let vmbc_path = {
155 let dll_path =
157 windows::core::s!(r#"C:\Program Files\Allied Vision\Vimba X\bin"#);
158 unsafe { windows::Win32::System::LibraryLoader::SetDllDirectoryA(dll_path) }?;
159 "VmbC.dll"
161 };
162
163 #[cfg(target_os = "linux")]
167 let vmbc_path = "/opt/VimbaX_2024-1/api/lib/libVmbC.so";
168
169 #[cfg(target_os = "macos")]
170 let vmbc_path = "/Library/Frameworks/VmbC.framework/Versions/A/VmbC";
171 std::path::PathBuf::from(vmbc_path)
172 }
173 };
174
175 Self::from_dynamic_lib_path(vimbac_path)
176 }
177
178 pub unsafe fn shutdown(&self) {
187 vimba_call_no_err!(self.vimba_lib.VmbShutdown());
188 }
189
190 pub fn from_dynamic_lib_path<P: AsRef<std::path::Path>>(
191 vimbac_path: P,
192 ) -> std::result::Result<Self, Error> {
193 let vimba_lib = match unsafe { vmbc_sys::VimbaC::new(vimbac_path.as_ref()) } {
194 Ok(vimba_lib) => vimba_lib,
195 Err(source) => {
196 let vimbac_path = vimbac_path.as_ref().to_path_buf();
197 return Err(Error::LibLoading {
198 source,
199 vimbac_path,
200 });
201 }
202 };
203
204 vimba_call!(vimba_lib.VmbStartup(std::ptr::null()))?;
205 Ok(VimbaLibrary {
206 vimba_lib,
207 started: true,
208 })
209 }
210
211 pub fn n_cameras(&self) -> Result<usize> {
212 let mut n_count = 0;
213 vimba_call!(
214 self.vimba_lib
215 .VmbCamerasList(std::ptr::null_mut(), 0, &mut n_count, 0)
216 )?;
217 Ok(n_count as usize)
218 }
219
220 pub fn camera_info(&self, n_count: usize) -> Result<Vec<CameraInfo>> {
221 let mut cameras: Vec<VmbCameraInfo_t> = vec![
222 VmbCameraInfo_t {
223 cameraIdString: std::ptr::null_mut(),
224 cameraIdExtended: std::ptr::null_mut(),
225 cameraName: std::ptr::null_mut(),
226 modelName: std::ptr::null_mut(),
227 serialString: std::ptr::null_mut(),
228 transportLayerHandle: std::ptr::null_mut(),
229 interfaceHandle: std::ptr::null_mut(),
230 localDeviceHandle: std::ptr::null_mut(),
231 streamHandles: std::ptr::null_mut(),
232 streamCount: 0,
233 permittedAccess: 0,
234 };
235 n_count
236 ];
237
238 let mut n_found_count = 0;
239 vimba_call!(self.vimba_lib.VmbCamerasList(
240 cameras[..].as_mut_ptr(),
241 n_count.try_into().unwrap(),
242 &mut n_found_count,
243 std::mem::size_of::<VmbCameraInfo_t>().try_into().unwrap()
244 ))?;
245
246 let result = cameras
247 .into_iter()
248 .map(|ci| CameraInfo {
249 camera_id_string: unsafe { std::ffi::CStr::from_ptr(ci.cameraIdString).to_str() }
250 .unwrap()
251 .to_string(),
252 camera_id_extended: unsafe {
253 std::ffi::CStr::from_ptr(ci.cameraIdExtended).to_str()
254 }
255 .unwrap()
256 .to_string(),
257 camera_name: unsafe { std::ffi::CStr::from_ptr(ci.cameraName).to_str() }
258 .unwrap()
259 .to_string(),
260 model_name: unsafe { std::ffi::CStr::from_ptr(ci.modelName).to_str() }
261 .unwrap()
262 .to_string(),
263 serial_string: unsafe { std::ffi::CStr::from_ptr(ci.serialString).to_str() }
264 .unwrap()
265 .to_string(),
266 permitted_access: AccessMode::new(ci.permittedAccess),
267 })
268 .collect();
269 Ok(result)
270 }
271}
272
273impl Drop for VimbaLibrary {
274 fn drop(&mut self) {
275 if self.started {
276 vimba_call_no_err!(self.vimba_lib.VmbShutdown());
277 self.started = false;
278 }
279 }
280}
281
282pub struct VersionInfo {
283 pub major: u32,
284 pub minor: u32,
285 pub patch: u32,
286}
287
288impl VersionInfo {
289 pub fn new(vimba_c: &vmbc_sys::VimbaC) -> Result<Self> {
290 let mut version_info = VmbVersionInfo_t {
291 major: 0,
292 minor: 0,
293 patch: 0,
294 };
295 vimba_call!(vimba_c.VmbVersionQuery(
296 &mut version_info,
297 std::mem::size_of::<VmbVersionInfo_t>() as u32
298 ))?;
299 Ok(Self {
300 major: version_info.major,
301 minor: version_info.minor,
302 patch: version_info.patch,
303 })
304 }
305}
306
307#[derive(Debug)]
308pub struct AccessMode {
309 code: u32,
310}
311
312impl AccessMode {
313 pub fn new(code: u32) -> Self {
314 Self { code }
315 }
316 pub fn as_u32(&self) -> u32 {
317 self.code
318 }
319}
320
321pub mod access_mode {
322 use vmbc_sys::VmbAccessModeType::*;
323 pub const FULL: crate::AccessMode = crate::AccessMode {
324 code: VmbAccessModeFull,
325 };
326}
327
328#[derive(Debug)]
329pub struct CameraInfo {
330 pub camera_id_string: String,
331 pub camera_id_extended: String,
332 pub camera_name: String,
333 pub model_name: String,
334 pub serial_string: String,
335 pub permitted_access: AccessMode,
336}
337
338pub struct Camera<'lib> {
339 handle: VmbHandle_t,
340 is_open: bool,
341 vimba_lib: &'lib vmbc_sys::VimbaC,
342}
343
344unsafe impl<'lib> Send for Camera<'lib> {}
345
346fn _test_camera_is_send() {
347 fn implements<T: Send>() {}
349 implements::<Camera>();
350}
351
352impl<'lib> std::fmt::Debug for Camera<'lib> {
353 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
354 write!(fmt, "Camera {{")?;
355 write!(fmt, " self.handle {:p},", self.handle)?;
356 write!(fmt, "}}")?;
357 Ok(())
358 }
359}
360
361impl<'lib> Camera<'lib> {
362 pub fn open(
363 camera_id: &str,
364 access_mode: AccessMode,
365 vimba_lib: &'lib vmbc_sys::VimbaC,
366 ) -> Result<Self> {
367 let data = std::ffi::CString::new(camera_id)?;
368 let mut handle = std::mem::MaybeUninit::<VmbHandle_t>::uninit();
369 vimba_call!(vimba_lib.VmbCameraOpen(
370 data.as_ptr(),
371 access_mode.as_u32(),
372 handle.as_mut_ptr()
373 ))?;
374 let handle = unsafe { handle.assume_init() };
375 let result = Self {
376 handle,
377 is_open: true,
378 vimba_lib,
379 };
380 tracing::debug!("opening {:?}", result);
381 Ok(result)
382 }
383
384 pub fn close(mut self) -> Result<()> {
385 if self.is_open {
386 vimba_call!(self.vimba_lib.VmbCameraClose(self.handle))?;
387 }
388 self.is_open = false; Ok(())
390 }
391
392 pub fn handle(&self) -> VmbHandle_t {
393 self.handle
394 }
395
396 pub fn feature_enum(&self, feature_name: &str) -> Result<&'static str> {
397 let mut result: *const std::os::raw::c_char = std::ptr::null_mut();
398 let data = std::ffi::CString::new(feature_name)?;
399 vimba_call!(
400 self.vimba_lib
401 .VmbFeatureEnumGet(self.handle, data.as_ptr(), &mut result)
402 )?;
403 Ok(unsafe { std::ffi::CStr::from_ptr(result).to_str()? })
404 }
405
406 pub fn feature_enum_range_query(&self, feature_name: &str) -> Result<Vec<String>> {
407 let name = std::ffi::CString::new(feature_name)?;
408 let mut num_filled = 0;
409 vimba_call!(self.vimba_lib.VmbFeatureEnumRangeQuery(
411 self.handle,
412 name.as_ptr(),
413 std::ptr::null_mut(),
414 0,
415 &mut num_filled,
416 ))?;
417
418 let mut p_name_array = vec![std::ptr::null(); num_filled.try_into().unwrap()];
419
420 let mut num_final = 0;
421 vimba_call!(self.vimba_lib.VmbFeatureEnumRangeQuery(
422 self.handle,
423 name.as_ptr(),
424 p_name_array.as_mut_ptr(),
425 num_filled,
426 &mut num_final,
427 ))?;
428
429 (0..num_final as usize)
430 .map(|i| {
431 let c_str_ptr = p_name_array[i];
432 let value = unsafe { std::ffi::CStr::from_ptr(c_str_ptr) }
433 .to_str()?
434 .to_string();
435 Ok(value)
436 })
437 .collect()
438 }
439
440 pub fn feature_enum_set(&self, feature_name: &str, value: &str) -> Result<()> {
441 let value_c = std::ffi::CString::new(value)?;
442 let name = std::ffi::CString::new(feature_name)?;
443 vimba_call!(self.vimba_lib.VmbFeatureEnumSet(
444 self.handle,
445 name.as_ptr(),
446 value_c.as_ptr()
447 ))?;
448 Ok(())
449 }
450
451 pub fn feature_access_query(&self, name: &str) -> Result<(bool, bool)> {
480 let mut is_readable = 0;
481 let mut is_writeable = 0;
482 vimba_call!(self.vimba_lib.VmbFeatureAccessQuery(
483 self.handle,
484 name.as_ptr() as _,
485 &mut is_readable,
486 &mut is_writeable,
487 ))?;
488
489 Ok((is_readable != 0, is_writeable != 0))
490 }
491
492 pub fn feature_string_set(&self, feature_name: &str, value: &str) -> Result<()> {
500 let value_c = std::ffi::CString::new(value)?;
501 let name = std::ffi::CString::new(feature_name)?;
502 vimba_call!(self.vimba_lib.VmbFeatureStringSet(
503 self.handle,
504 name.as_ptr(),
505 value_c.as_ptr()
506 ))?;
507 Ok(())
508 }
509
510 pub fn feature_int(&self, feature_name: &str) -> Result<i64> {
511 let mut result = 0;
512 let data = std::ffi::CString::new(feature_name)?;
513 vimba_call!(
514 self.vimba_lib
515 .VmbFeatureIntGet(self.handle, data.as_ptr(), &mut result)
516 )?;
517 Ok(result)
518 }
519
520 pub fn feature_int_set(&self, feature_name: &str, value: i64) -> Result<()> {
521 let data = std::ffi::CString::new(feature_name)?;
522 vimba_call!(
523 self.vimba_lib
524 .VmbFeatureIntSet(self.handle, data.as_ptr(), value)
525 )?;
526 Ok(())
527 }
528
529 pub fn feature_float(&self, feature_name: &str) -> Result<f64> {
530 let mut result = 0.0;
531 let data = std::ffi::CString::new(feature_name)?;
532 vimba_call!(
533 self.vimba_lib
534 .VmbFeatureFloatGet(self.handle, data.as_ptr(), &mut result)
535 )?;
536 Ok(result)
537 }
538
539 pub fn feature_float_set(&self, feature_name: &str, value: f64) -> Result<()> {
540 let data = std::ffi::CString::new(feature_name)?;
541 vimba_call!(
542 self.vimba_lib
543 .VmbFeatureFloatSet(self.handle, data.as_ptr(), value)
544 )?;
545 Ok(())
546 }
547
548 pub fn feature_float_range_query(&self, feature_name: &str) -> Result<(f64, f64)> {
549 let mut min = 0.0;
550 let mut max = 0.0;
551 let data = std::ffi::CString::new(feature_name)?;
552 vimba_call!(self.vimba_lib.VmbFeatureFloatRangeQuery(
553 self.handle,
554 data.as_ptr(),
555 &mut min,
556 &mut max
557 ))?;
558 Ok((min, max))
559 }
560
561 pub fn feature_boolean(&self, feature_name: &str) -> Result<bool> {
562 let mut result = 0;
563 let data = std::ffi::CString::new(feature_name)?;
564 vimba_call!(
565 self.vimba_lib
566 .VmbFeatureBoolGet(self.handle, data.as_ptr(), &mut result)
567 )?;
568 Ok(result != 0)
569 }
570 pub fn feature_boolean_set(&self, feature_name: &str, value: bool) -> Result<()> {
571 let value_u8 = if value { 1 } else { 0 };
572 let data = std::ffi::CString::new(feature_name)?;
573 vimba_call!(
574 self.vimba_lib
575 .VmbFeatureBoolSet(self.handle, data.as_ptr(), value_u8)
576 )?;
577 Ok(())
578 }
579
580 pub fn command_run(&self, command_name: &str) -> Result<()> {
581 tracing::debug!("camera {:?} command_run {}", self, command_name);
582 let data = std::ffi::CString::new(command_name)?;
583 vimba_call!(
584 self.vimba_lib
585 .VmbFeatureCommandRun(self.handle, data.as_ptr())
586 )?;
587 Ok(())
588 }
589
590 pub fn pixel_format(&self) -> Result<formats::pixel_format::PixFmt> {
591 let pixel_format = self.feature_enum("PixelFormat")?;
592 str_to_pixel_format(pixel_format)
593 }
594
595 pub fn allocate_buffer(&self) -> Result<Vec<u8>> {
596 let payload_size = self.feature_int("PayloadSize")?;
597 Ok(vec![0u8; payload_size.try_into().unwrap()])
598 }
599
600 pub fn frame_announce(&self, frame: &mut Frame) -> Result<()> {
601 if frame.already_announced {
602 return Err(Error::InvalidCall {});
603 }
604
605 tracing::debug!("camera {:?} announcing frame {:?}", self, frame);
606
607 vimba_call!(self.vimba_lib.VmbFrameAnnounce(
608 self.handle,
609 &*frame.frame,
610 std::mem::size_of::<VmbFrame_t>().try_into().unwrap()
611 ))?;
612
613 frame.already_announced = true;
614 Ok(())
615 }
616
617 pub fn frame_revoke(&self, frame: &mut Frame) -> Result<()> {
618 tracing::debug!("camera {:?} revoking frame {:?}", self, frame);
619 vimba_call!(self.vimba_lib.VmbFrameRevoke(self.handle, &*frame.frame,))?;
620 frame.already_announced = false;
621 Ok(())
622 }
623
624 pub fn capture_start(&self) -> Result<()> {
625 tracing::debug!("camera {:?} capture start", self);
626 vimba_call!(self.vimba_lib.VmbCaptureStart(self.handle))?;
627 Ok(())
628 }
629
630 pub fn capture_end(&self) -> Result<()> {
631 vimba_call!(self.vimba_lib.VmbCaptureEnd(self.handle))?;
632 Ok(())
633 }
634
635 pub fn capture_frame_queue(&self, frame: &mut Frame) -> Result<()> {
636 tracing::debug!("camera {:?} queueing frame {:?}", self, frame);
637 vimba_call!(
638 self.vimba_lib
639 .VmbCaptureFrameQueue(self.handle, &*frame.frame, None)
640 )?;
641 Ok(())
642 }
643 pub fn capture_frame_queue_with_callback(
644 &self,
645 frame: &mut Frame,
646 callback: VmbFrameCallback,
647 ) -> Result<()> {
648 tracing::debug!("camera {:?} queueing frame {:?}", self, frame);
649 vimba_call!(
650 self.vimba_lib
651 .VmbCaptureFrameQueue(self.handle, &*frame.frame, callback)
652 )?;
653 Ok(())
654 }
655
656 pub fn capture_queue_flush(&self) -> Result<()> {
657 vimba_call!(self.vimba_lib.VmbCaptureQueueFlush(self.handle))?;
658 Ok(())
659 }
660
661 pub fn capture_frame_wait(&self, frame: &mut Frame, timeout: u32) -> Result<()> {
662 tracing::debug!("camera {:?} waiting for frame {:?}", self, frame);
663 vimba_call!(
664 self.vimba_lib
665 .VmbCaptureFrameWait(self.handle, &*frame.frame, timeout)
666 )?;
667 Ok(())
668 }
669
670 pub fn camera_settings_save<P: AsRef<std::path::Path>>(
671 &self,
672 out_path: P,
673 p_settings: &VmbFeaturePersistSettings_t,
674 ) -> Result<()> {
675 let mut buf = path_to_bytes(out_path);
676 buf.push(0);
677 let sz = std::mem::size_of::<VmbFeaturePersistSettings_t>();
678 let sz = sz.try_into().unwrap(); vimba_call!(self.vimba_lib.VmbSettingsSave(
680 self.handle,
681 buf.as_ptr() as *const i8,
682 p_settings as *const _,
683 sz
684 ))?;
685 Ok(())
686 }
687
688 pub fn camera_settings_load<P: AsRef<std::path::Path>>(
689 &self,
690 in_path: P,
691 p_settings: &VmbFeaturePersistSettings_t,
692 ) -> Result<()> {
693 let mut buf = path_to_bytes(in_path);
694 buf.push(0);
695 let sz = std::mem::size_of::<VmbFeaturePersistSettings_t>();
696 let sz = sz.try_into().unwrap(); vimba_call!(self.vimba_lib.VmbSettingsLoad(
698 self.handle,
699 buf.as_ptr() as *const i8,
700 p_settings as *const _,
701 sz
702 ))?;
703 Ok(())
704 }
705}
706
707impl<'lib> Drop for Camera<'lib> {
708 fn drop(&mut self) {
709 if self.is_open {
710 vimba_call!(self.vimba_lib.VmbCameraClose(self.handle)).unwrap();
711 self.is_open = false;
712 }
713 }
714}
715
716pub fn default_feature_persist_settings() -> VmbFeaturePersistSettings_t {
717 VmbFeaturePersistSettings_t {
722 persistType: vmbc_sys::VmbFeaturePersistType::VmbFeaturePersistStreamable,
723 modulePersistFlags: vmbc_sys::VmbModulePersistFlagsType::VmbModulePersistFlagsNone,
724 maxIterations: 10,
725 loggingLevel: 4,
726 }
727}
728
729pub struct Frame {
731 buffer: Vec<u8>,
732 frame: Pin<Box<VmbFrame_t>>,
735 already_announced: bool,
736}
737
738unsafe impl Send for Frame {}
739
740fn _test_frame_is_send() {
741 fn implements<T: Send>() {}
743 implements::<Frame>();
744}
745
746impl std::fmt::Debug for Frame {
747 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
748 write!(fmt, "Frame {{")?;
749 write!(
750 fmt,
751 " frame as *const VmbFrame_t {:p},",
752 &*self.frame as *const VmbFrame_t
753 )?;
754 write!(fmt, " buffer.as_ptr() {:p},", self.buffer.as_ptr())?;
755 write!(fmt, " buffer.len() {},", self.buffer.len())?;
756 write!(fmt, " frame.buffer {:p},", self.frame.buffer)?;
757 write!(fmt, " frame.bufferSize {},", self.frame.bufferSize)?;
758 write!(fmt, " already_announced {:?},", self.already_announced)?;
759 write!(fmt, "}}")?;
760 Ok(())
761 }
762}
763
764impl Frame {
765 pub fn new(mut buffer: Vec<u8>) -> Self {
766 let frame = Box::pin(VmbFrame_t {
767 buffer: buffer.as_mut_ptr() as _,
768 bufferSize: buffer.len().try_into().unwrap(),
769 context: [std::ptr::null_mut(); 4],
770 receiveStatus: 0,
771 frameID: 0,
772 timestamp: 0,
773 imageData: std::ptr::null_mut(),
774 receiveFlags: 0,
775 pixelFormat: 0,
776 width: 0,
777 height: 0,
778 offsetX: 0,
779 offsetY: 0,
780 payloadType: vmbc_sys::VmbPayloadType::VmbPayloadTypeUnknown,
781 chunkDataPresent: 0, });
783
784 Self {
785 buffer,
786 frame,
787 already_announced: false,
788 }
789 }
790 #[inline]
791 pub fn is_complete(&self) -> bool {
792 self.frame.receiveStatus == VmbFrameStatusType::VmbFrameStatusComplete
793 }
794 #[inline]
795 pub fn width(&self) -> u32 {
796 self.frame.width
797 }
798 #[inline]
799 pub fn height(&self) -> u32 {
800 self.frame.height
801 }
802 #[inline]
803 pub fn buffer_size(&self) -> usize {
804 self.frame.bufferSize.try_into().unwrap()
805 }
806 #[inline]
807 pub fn buffer(&self) -> &[u8] {
808 &self.buffer[..self.buffer_size()]
809 }
810 #[inline]
811 pub fn frame_id(&self) -> u64 {
812 self.frame.frameID
813 }
814 #[inline]
815 pub fn timestamp(&self) -> u64 {
816 self.frame.timestamp
817 }
818 #[inline]
819 pub fn pixel_format(&self) -> Result<formats::PixFmt> {
820 pixel_format_code(self.frame.pixelFormat)
821 }
822}
823
824pub fn pixel_format_code(code: u32) -> Result<formats::PixFmt> {
825 use formats::PixFmt::*;
826 use vmbc_sys::VmbPixelFormatType::*;
827 #[expect(non_upper_case_globals)]
828 let fmt = match code {
829 VmbPixelFormatMono8 => Mono8,
830 VmbPixelFormatBayerGR8 => BayerGR8,
831 VmbPixelFormatBayerRG8 => BayerRG8,
832 VmbPixelFormatBayerGB8 => BayerGB8,
833 VmbPixelFormatBayerBG8 => BayerBG8,
834 VmbPixelFormatRgb8 => RGB8,
835 _code_signed => {
841 return Err(Error::UnknownPixelFormatCode { code });
842 }
843 };
844 Ok(fmt)
845}
846
847pub fn str_to_pixel_format(pixel_format: &str) -> Result<formats::pixel_format::PixFmt> {
848 use formats::pixel_format::PixFmt::*;
849 Ok(match pixel_format {
850 "Mono8" => Mono8,
851 "RGB8" => RGB8,
852 "BayerRG8" => BayerRG8,
853 fmt => {
859 return Err(Error::UnknownPixelFormat {
860 fmt: fmt.to_string(),
861 });
862 }
863 })
864}
865
866pub fn pixel_format_to_str(pixfmt: formats::pixel_format::PixFmt) -> Result<&'static str> {
867 use formats::pixel_format::PixFmt::*;
868 Ok(match pixfmt {
869 Mono8 => "Mono8",
870 RGB8 => "RGB8",
871 _ => {
877 return Err(Error::UnknownPixelFormat {
878 fmt: format!("pixfmt {:?}", pixfmt),
879 });
880 }
881 })
882}
883
884#[cfg(unix)]
974fn path_to_bytes<P: AsRef<std::path::Path>>(path: P) -> Vec<u8> {
975 use std::os::unix::ffi::OsStrExt;
976 path.as_ref().as_os_str().as_bytes().to_vec()
977}
978
979#[cfg(not(unix))]
983fn path_to_bytes<P: AsRef<std::path::Path>>(path: P) -> Vec<u8> {
984 path.as_ref().to_string_lossy().to_string().into_bytes()
990}