1use std::{collections::HashMap, ffi::CStr, marker::PhantomData, ptr::NonNull};
2
3use libcamera_control_direction::*;
4use libcamera_sys::*;
5use num_enum::{IntoPrimitive, TryFromPrimitive};
6use thiserror::Error;
7
8use crate::{
9 control_value::{ControlType, ControlValue, ControlValueError},
10 controls::{self, ControlId},
11 properties::{self, PropertyId},
12 utils::{UniquePtr, UniquePtrTarget},
13};
14
15#[derive(Debug, Error)]
16pub enum ControlError {
17 #[error("Control id {0} not found")]
18 NotFound(u32),
19 #[error("Control value error: {0}")]
20 ValueError(#[from] ControlValueError),
21}
22
23pub trait ControlEntry:
24 Clone + Into<ControlValue> + TryFrom<ControlValue, Error = ControlValueError> + core::fmt::Debug
25{
26 const ID: u32;
27}
28
29pub trait Control: ControlEntry {}
30pub trait Property: ControlEntry {}
31
32pub trait DynControlEntry: core::fmt::Debug {
34 fn id(&self) -> u32;
35 fn value(&self) -> ControlValue;
36}
37
38impl<T: ControlEntry> DynControlEntry for T {
39 fn id(&self) -> u32 {
40 Self::ID
41 }
42
43 fn value(&self) -> ControlValue {
44 self.clone().into()
45 }
46}
47
48#[repr(transparent)]
49pub struct ControlInfo(libcamera_control_info_t);
50
51#[repr(transparent)]
52pub struct ControlIdMap(libcamera_control_id_map_t);
53
54impl ControlIdMap {
55 pub(crate) unsafe fn from_ptr<'a>(ptr: NonNull<libcamera_control_id_map_t>) -> &'a mut Self {
56 &mut *(ptr.as_ptr() as *mut Self)
57 }
58
59 pub fn iter(&self) -> Option<ControlIdMapIter<'_>> {
60 ControlIdMapIter::new(self)
61 }
62
63 pub(crate) fn ptr(&self) -> *const libcamera_control_id_map_t {
64 &self.0 as *const libcamera_control_id_map_t
65 }
66}
67
68#[derive(Clone, Copy)]
69pub struct ControlIdRef {
70 ptr: NonNull<libcamera_control_id_t>,
71}
72
73impl ControlIdRef {
74 pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_control_id_t>) -> Self {
75 Self { ptr }
76 }
77
78 pub fn id(&self) -> u32 {
79 unsafe { libcamera_control_id(self.ptr.as_ptr()) as u32 }
80 }
81
82 pub fn name(&self) -> &str {
83 unsafe {
84 CStr::from_ptr(libcamera_control_name(self.ptr.as_ptr()))
85 .to_str()
86 .unwrap()
87 }
88 }
89
90 pub fn vendor(&self) -> &str {
91 unsafe {
92 CStr::from_ptr(libcamera_control_id_vendor(self.ptr.as_ptr()))
93 .to_str()
94 .unwrap()
95 }
96 }
97
98 pub fn ty(&self) -> ControlType {
99 unsafe { libcamera_control_id_type(self.ptr.as_ptr()) }
100 .try_into()
101 .unwrap_or(ControlType::None)
102 }
103
104 pub fn is_array(&self) -> bool {
105 unsafe { libcamera_control_id_is_array(self.ptr.as_ptr()) }
106 }
107
108 pub fn size(&self) -> usize {
109 unsafe { libcamera_control_id_size(self.ptr.as_ptr()) }
110 }
111}
112
113impl ControlInfo {
114 pub(crate) unsafe fn from_ptr<'a>(ptr: NonNull<libcamera_control_info_t>) -> &'a mut Self {
115 &mut *(ptr.as_ptr() as *mut Self)
117 }
118
119 pub(crate) fn ptr(&self) -> *const libcamera_control_info_t {
120 &self.0 as *const libcamera_control_info_t
122 }
123
124 pub fn min(&self) -> ControlValue {
125 unsafe {
126 ControlValue::read(NonNull::new(libcamera_control_info_min(self.ptr().cast_mut()).cast_mut()).unwrap())
127 .unwrap()
128 }
129 }
130
131 pub fn max(&self) -> ControlValue {
132 unsafe {
133 ControlValue::read(NonNull::new(libcamera_control_info_max(self.ptr().cast_mut()).cast_mut()).unwrap())
134 .unwrap()
135 }
136 }
137
138 pub fn def(&self) -> ControlValue {
139 unsafe {
140 ControlValue::read(NonNull::new(libcamera_control_info_def(self.ptr().cast_mut()).cast_mut()).unwrap())
141 .unwrap()
142 }
143 }
144
145 pub fn values(&self) -> Vec<ControlValue> {
146 unsafe {
147 let mut size: usize = 0;
148 let values_ptr = libcamera_control_info_values(self.ptr(), &mut size as *mut usize);
149
150 if values_ptr.is_null() || size == 0 {
151 return Vec::new();
152 }
153
154 let control_value_size = libcamera_control_value_size();
156
157 let base_ptr = values_ptr as *const u8;
159
160 let mut control_values = Vec::with_capacity(size);
161 for i in 0..size {
162 let offset = i * control_value_size;
164 let val_ptr = base_ptr.add(offset) as *const libcamera_control_value_t;
165
166 if val_ptr.is_null() {
167 eprintln!("ControlValue at index {i} is null");
168 continue;
169 }
170
171 match ControlValue::read(NonNull::new(val_ptr.cast_mut()).unwrap()) {
173 Ok(control_val) => control_values.push(control_val),
174 Err(e) => {
175 eprintln!("Failed to read ControlValue at index {i}: {e:?}");
176 }
177 }
178 }
179
180 control_values
181 }
182 }
183}
184
185impl core::fmt::Debug for ControlInfo {
186 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187 f.debug_struct("ControlInfo")
188 .field("min", &self.min())
189 .field("max", &self.max())
190 .field("def", &self.def())
191 .field("values", &self.values())
192 .finish()
193 }
194}
195
196#[repr(transparent)]
197pub struct ControlInfoMap(libcamera_control_info_map_t);
198
199impl ControlInfoMap {
200 pub(crate) unsafe fn from_ptr<'a>(ptr: NonNull<libcamera_control_info_map_t>) -> &'a mut Self {
201 &mut *(ptr.as_ptr() as *mut Self)
203 }
204
205 pub(crate) fn ptr(&self) -> *const libcamera_control_info_map_t {
206 &self.0 as *const libcamera_control_info_map_t
208 }
209
210 pub fn at(&self, key: u32) -> Result<&ControlInfo, ControlError> {
211 unsafe {
212 let ptr = NonNull::new(libcamera_control_info_map_at(self.ptr().cast_mut(), key).cast_mut());
213 match ptr {
214 Some(ptr) => Ok(ControlInfo::from_ptr(ptr)),
215 None => Err(ControlError::NotFound(key)),
216 }
217 }
218 }
219
220 pub fn count(&self, key: u32) -> usize {
221 unsafe { libcamera_control_info_map_count(self.ptr().cast_mut(), key) }
222 }
223
224 pub fn find(&self, key: u32) -> Result<&ControlInfo, ControlError> {
225 unsafe {
226 let ptr = NonNull::new(libcamera_control_info_map_find(self.ptr().cast_mut(), key).cast_mut());
227
228 match ptr {
229 Some(ptr) => Ok(ControlInfo::from_ptr(ptr)),
230 None => Err(ControlError::NotFound(key)),
231 }
232 }
233 }
234
235 pub fn size(&self) -> usize {
236 unsafe { libcamera_control_info_map_size(self.ptr().cast_mut()) }
237 }
238}
239
240impl<'a> IntoIterator for &'a ControlInfoMap {
241 type Item = (u32, &'a ControlInfo);
242 type IntoIter = ControlInfoMapIter<'a>;
243
244 fn into_iter(self) -> Self::IntoIter {
245 ControlInfoMapIter::new(self).expect("Failed to create ControlInfoMap iterator")
246 }
247}
248
249impl core::fmt::Debug for ControlInfoMap {
250 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251 let mut dm = f.debug_map();
252 for (key, value) in self.into_iter() {
253 match ControlId::try_from(key) {
254 Ok(id) => dm.entry(&id.name(), value),
255 Err(_) => dm.entry(&key, value),
256 };
257 }
258 dm.finish()
259 }
260}
261
262#[repr(transparent)]
263pub struct ControlList(libcamera_control_list_t);
264
265#[derive(Debug, Clone, Copy)]
267#[repr(u32)]
268pub enum MergePolicy {
269 KeepExisting = libcamera_control_merge_policy::LIBCAMERA_CONTROL_MERGE_KEEP_EXISTING,
270 OverwriteExisting = libcamera_control_merge_policy::LIBCAMERA_CONTROL_MERGE_OVERWRITE_EXISTING,
271}
272
273impl UniquePtrTarget for ControlList {
274 unsafe fn ptr_new() -> *mut Self {
275 libcamera_control_list_create() as *mut Self
276 }
277
278 unsafe fn ptr_drop(ptr: *mut Self) {
279 libcamera_control_list_destroy(ptr as *mut libcamera_control_list_t)
280 }
281}
282
283impl ControlList {
284 pub fn new() -> UniquePtr<Self> {
285 UniquePtr::new()
286 }
287
288 pub fn from_id_map(map: &ControlIdMap) -> Option<UniquePtr<Self>> {
289 unsafe {
290 let ptr = libcamera_control_list_create_with_idmap(map.ptr());
291 UniquePtr::from_raw(ptr as *mut Self)
292 }
293 }
294
295 pub fn from_info_map(info_map: &ControlInfoMap) -> Option<UniquePtr<Self>> {
296 unsafe {
297 let ptr = libcamera_control_list_create_with_info_map(info_map.ptr());
298 UniquePtr::from_raw(ptr as *mut Self)
299 }
300 }
301
302 pub(crate) unsafe fn from_ptr<'a>(ptr: NonNull<libcamera_control_list_t>) -> &'a mut Self {
303 &mut *(ptr.as_ptr() as *mut Self)
305 }
306
307 pub(crate) fn ptr(&self) -> *const libcamera_control_list_t {
308 &self.0 as *const libcamera_control_list_t
310 }
311
312 pub fn len(&self) -> usize {
313 unsafe { libcamera_control_list_size(self.ptr().cast_mut()) }
314 }
315
316 pub fn is_empty(&self) -> bool {
317 unsafe { libcamera_control_list_is_empty(self.ptr().cast_mut()) }
318 }
319
320 pub fn clear(&mut self) {
321 unsafe { libcamera_control_list_clear(self.ptr().cast_mut()) }
322 }
323
324 pub fn contains(&self, id: u32) -> bool {
325 unsafe { libcamera_control_list_contains(self.ptr(), id) }
326 }
327
328 pub fn merge(&mut self, other: &ControlList, policy: MergePolicy) {
329 unsafe { libcamera_control_list_merge(self.ptr().cast_mut(), other.ptr(), policy as u32) }
330 }
331
332 pub fn info_map(&self) -> Option<&ControlInfoMap> {
333 unsafe {
334 let ptr = libcamera_control_list_info_map(self.ptr());
335 NonNull::new(ptr.cast_mut()).map(|p| {
336 let m: &mut ControlInfoMap = ControlInfoMap::from_ptr(p);
337 &*m
338 })
339 }
340 }
341
342 pub fn id_map(&self) -> Option<&ControlIdMap> {
343 unsafe {
344 let ptr = libcamera_control_list_id_map(self.ptr());
345 NonNull::new(ptr.cast_mut()).map(|p| {
346 let m: &mut ControlIdMap = ControlIdMap::from_ptr(p);
347 &*m
348 })
349 }
350 }
351
352 pub fn get<C: Control>(&self) -> Result<C, ControlError> {
353 let val_ptr = NonNull::new(unsafe { libcamera_control_list_get(self.ptr().cast_mut(), C::ID as _).cast_mut() })
354 .ok_or(ControlError::NotFound(C::ID))?;
355
356 let val = unsafe { ControlValue::read(val_ptr) }?;
357 Ok(C::try_from(val)?)
358 }
359
360 pub fn set<C: Control>(&mut self, val: C) -> Result<(), ControlError> {
365 let ctrl_val: ControlValue = val.into();
366
367 unsafe {
368 let val_ptr = NonNull::new(libcamera_control_value_create()).unwrap();
369 ctrl_val.write(val_ptr);
370 libcamera_control_list_set(self.ptr().cast_mut(), C::ID as _, val_ptr.as_ptr());
371 libcamera_control_value_destroy(val_ptr.as_ptr());
372 }
373
374 Ok(())
375 }
376
377 pub fn set_raw(&mut self, id: u32, val: ControlValue) -> Result<(), ControlError> {
382 unsafe {
383 let val_ptr = NonNull::new(libcamera_control_value_create()).unwrap();
384 val.write(val_ptr);
385 libcamera_control_list_set(self.ptr().cast_mut(), id as _, val_ptr.as_ptr());
386 libcamera_control_value_destroy(val_ptr.as_ptr());
387 }
388
389 Ok(())
390 }
391
392 pub fn get_raw(&mut self, id: u32) -> Result<ControlValue, ControlError> {
393 let val_ptr = NonNull::new(unsafe { libcamera_control_list_get(self.ptr().cast_mut(), id as _).cast_mut() })
394 .ok_or(ControlError::NotFound(id))?;
395
396 let val = unsafe { ControlValue::read(val_ptr) }?;
397 Ok(val)
398 }
399}
400
401impl<'d> IntoIterator for &'d ControlList {
402 type Item = (u32, ControlValue);
403
404 type IntoIter = ControlListRefIterator<'d>;
405
406 fn into_iter(self) -> Self::IntoIter {
407 ControlListRefIterator {
408 it: NonNull::new(unsafe { libcamera_control_list_iter(self.ptr().cast_mut()) }).unwrap(),
409 _phantom: Default::default(),
410 }
411 }
412}
413
414impl core::fmt::Debug for ControlList {
415 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416 let mut map = f.debug_map();
417 for (id, val) in self.into_iter() {
418 match ControlId::try_from(id) {
419 Ok(id) => match controls::make_dyn(id, val.clone()) {
421 Ok(val) => map.entry(&id, &val),
422 Err(_) => map.entry(&id, &val),
423 },
424 Err(_) => map.entry(&id, &val),
426 };
427 }
428 map.finish()
429 }
430}
431
432#[repr(transparent)]
433pub struct PropertyList(libcamera_control_list_t);
434
435impl PropertyList {
436 pub(crate) unsafe fn from_ptr<'a>(ptr: NonNull<libcamera_control_list_t>) -> &'a mut Self {
437 &mut *(ptr.as_ptr() as *mut Self)
439 }
440
441 pub(crate) fn ptr(&self) -> *const libcamera_control_list_t {
442 &self.0 as *const libcamera_control_list_t
444 }
445
446 pub fn get<C: Property>(&self) -> Result<C, ControlError> {
447 let val_ptr = NonNull::new(unsafe { libcamera_control_list_get(self.ptr().cast_mut(), C::ID as _).cast_mut() })
448 .ok_or(ControlError::NotFound(C::ID))?;
449
450 let val = unsafe { ControlValue::read(val_ptr) }?;
451
452 Ok(C::try_from(val)?)
453 }
454}
455
456impl<'d> IntoIterator for &'d PropertyList {
457 type Item = (u32, ControlValue);
458
459 type IntoIter = ControlListRefIterator<'d>;
460
461 fn into_iter(self) -> Self::IntoIter {
462 ControlListRefIterator {
463 it: NonNull::new(unsafe { libcamera_control_list_iter(self.ptr().cast_mut()) }).unwrap(),
464 _phantom: Default::default(),
465 }
466 }
467}
468
469impl core::fmt::Debug for PropertyList {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 let mut map = f.debug_map();
472 for (id, val) in self.into_iter() {
473 match PropertyId::try_from(id) {
474 Ok(id) => match properties::make_dyn(id, val.clone()) {
476 Ok(val) => map.entry(&id, &val),
477 Err(_) => map.entry(&id, &val),
478 },
479 Err(_) => map.entry(&id, &val),
481 };
482 }
483 map.finish()
484 }
485}
486
487pub struct ControlListRefIterator<'d> {
488 it: NonNull<libcamera_control_list_iter_t>,
489 _phantom: PhantomData<&'d ()>,
490}
491
492impl Iterator for ControlListRefIterator<'_> {
493 type Item = (u32, ControlValue);
494
495 fn next(&mut self) -> Option<Self::Item> {
496 if unsafe { libcamera_control_list_iter_end(self.it.as_ptr()) } {
497 None
498 } else {
499 let id = unsafe { libcamera_control_list_iter_id(self.it.as_ptr()) };
500 let val_ptr =
501 NonNull::new(unsafe { libcamera_control_list_iter_value(self.it.as_ptr()).cast_mut() }).unwrap();
502 let val = unsafe { ControlValue::read(val_ptr) }.unwrap();
503
504 unsafe { libcamera_control_list_iter_next(self.it.as_ptr()) };
505
506 Some((id, val))
507 }
508 }
509}
510
511impl Drop for ControlListRefIterator<'_> {
512 fn drop(&mut self) {
513 unsafe { libcamera_control_list_iter_destroy(self.it.as_ptr()) }
514 }
515}
516
517pub struct ControlInfoMapIter<'a> {
518 iter: *mut libcamera_control_info_map_iter_t,
519 marker: PhantomData<&'a libcamera_control_info_map_t>,
520}
521
522impl<'a> ControlInfoMapIter<'a> {
523 pub fn new(map: &'a ControlInfoMap) -> Option<Self> {
524 unsafe {
525 let iter = libcamera_control_info_map_iter_create(map.ptr());
526 if iter.is_null() {
527 None
528 } else {
529 Some(ControlInfoMapIter {
530 iter,
531 marker: PhantomData,
532 })
533 }
534 }
535 }
536}
537
538impl<'a> Iterator for ControlInfoMapIter<'a> {
539 type Item = (u32, &'a ControlInfo);
540
541 fn next(&mut self) -> Option<Self::Item> {
542 unsafe {
543 if libcamera_control_info_map_iter_has_next(self.iter) {
544 let key = libcamera_control_info_map_iter_key(self.iter);
545 let value_ptr = libcamera_control_info_map_iter_value(self.iter);
546 if value_ptr.is_null() {
547 None
548 } else {
549 let control_info = &*(value_ptr as *const ControlInfo);
550 libcamera_control_info_map_iter_next(self.iter);
551 Some((key, control_info))
552 }
553 } else {
554 None
555 }
556 }
557 }
558}
559
560impl<'a> Drop for ControlInfoMapIter<'a> {
561 fn drop(&mut self) {
562 unsafe {
563 libcamera_control_info_map_iter_destroy(self.iter);
564 }
565 }
566}
567
568pub struct ControlIdEnumeratorsIter<'a> {
569 iter: *mut libcamera_control_id_enumerators_iter_t,
570 marker: PhantomData<&'a ControlId>,
571}
572
573impl<'a> ControlIdEnumeratorsIter<'a> {
574 fn new(id: &'a ControlId) -> Option<Self> {
575 unsafe {
576 let iter = libcamera_control_id_enumerators_iter_create(id.as_ptr());
577 if iter.is_null() {
578 None
579 } else {
580 Some(ControlIdEnumeratorsIter {
581 iter,
582 marker: PhantomData,
583 })
584 }
585 }
586 }
587}
588
589impl Iterator for ControlIdEnumeratorsIter<'_> {
590 type Item = (i32, String);
591
592 fn next(&mut self) -> Option<Self::Item> {
593 unsafe {
594 if libcamera_control_id_enumerators_iter_has_next(self.iter) {
595 let key = libcamera_control_id_enumerators_iter_key(self.iter);
596 let val_ptr = libcamera_control_id_enumerators_iter_value(self.iter);
597 if val_ptr.is_null() {
598 None
599 } else {
600 let name = CStr::from_ptr(val_ptr).to_string_lossy().into_owned();
601 libcamera_control_id_enumerators_iter_next(self.iter);
602 Some((key, name))
603 }
604 } else {
605 None
606 }
607 }
608 }
609}
610
611impl Drop for ControlIdEnumeratorsIter<'_> {
612 fn drop(&mut self) {
613 unsafe {
614 libcamera_control_id_enumerators_iter_destroy(self.iter);
615 }
616 }
617}
618
619pub struct ControlIdMapIter<'a> {
620 iter: *mut libcamera_control_id_map_iter_t,
621 marker: PhantomData<&'a ControlIdMap>,
622}
623
624impl<'a> ControlIdMapIter<'a> {
625 fn new(map: &'a ControlIdMap) -> Option<Self> {
626 unsafe {
627 let iter = libcamera_control_id_map_iter_create(map.ptr());
628 if iter.is_null() {
629 None
630 } else {
631 Some(Self {
632 iter,
633 marker: PhantomData,
634 })
635 }
636 }
637 }
638}
639
640impl<'a> Iterator for ControlIdMapIter<'a> {
641 type Item = (u32, ControlIdRef);
642
643 fn next(&mut self) -> Option<Self::Item> {
644 unsafe {
645 if libcamera_control_id_map_iter_has_next(self.iter) {
646 let key = libcamera_control_id_map_iter_key(self.iter);
647 let id_ptr = libcamera_control_id_map_iter_value(self.iter);
648 libcamera_control_id_map_iter_next(self.iter);
649 NonNull::new(id_ptr.cast_mut()).map(|p| (key, ControlIdRef::from_ptr(p)))
650 } else {
651 None
652 }
653 }
654 }
655}
656
657impl Drop for ControlIdMapIter<'_> {
658 fn drop(&mut self) {
659 unsafe {
660 libcamera_control_id_map_iter_destroy(self.iter);
661 }
662 }
663}
664
665#[derive(Debug, Clone, Copy, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
666#[repr(u32)]
667pub enum ControlDirection {
668 In = LIBCAMERA_CONTROL_DIRECTION_IN,
670 Out = LIBCAMERA_CONTROL_DIRECTION_OUT,
672 InOut = LIBCAMERA_CONTROL_DIRECTION_IN | LIBCAMERA_CONTROL_DIRECTION_OUT,
674}
675
676impl ControlId {
677 pub fn name(&self) -> String {
678 unsafe { CStr::from_ptr(libcamera_control_name_from_id(self.id())) }
679 .to_str()
680 .unwrap()
681 .into()
682 }
683
684 fn as_ptr(&self) -> *mut libcamera_control_id_t {
685 let ptr = unsafe { libcamera_control_from_id(self.id()) as *mut libcamera_control_id_t };
686 assert!(!ptr.is_null(), "libcamera_control_from_id returned null");
687 ptr
688 }
689
690 pub fn vendor(&self) -> String {
691 unsafe {
692 let ctrl = self.as_ptr();
693 if ctrl.is_null() {
694 String::new()
695 } else {
696 let ptr = libcamera_control_id_vendor(ctrl);
697 CStr::from_ptr(ptr).to_string_lossy().to_string()
698 }
699 }
700 }
701
702 pub fn control_type(&self) -> ControlType {
703 let raw = unsafe { libcamera_control_id_type(self.as_ptr()) } as u32;
704 ControlType::try_from(raw).expect("Unknown ControlType")
705 }
706
707 pub fn direction(&self) -> ControlDirection {
708 let raw = unsafe { libcamera_control_id_direction(self.as_ptr()) } as u32;
709 ControlDirection::try_from(raw).expect("Unknown libcamera_control_direction value")
710 }
711
712 pub fn is_input(&self) -> bool {
713 unsafe { libcamera_control_id_is_input(self.as_ptr()) }
714 }
715
716 pub fn is_output(&self) -> bool {
717 unsafe { libcamera_control_id_is_output(self.as_ptr()) }
718 }
719
720 pub fn is_array(&self) -> bool {
721 unsafe { libcamera_control_id_is_array(self.as_ptr()) }
722 }
723
724 pub fn size(&self) -> usize {
725 unsafe { libcamera_control_id_size(self.as_ptr()) }
726 }
727
728 pub fn enumerators(&self) -> Option<ControlIdEnumeratorsIter<'_>> {
729 ControlIdEnumeratorsIter::new(self)
730 }
731
732 pub fn enumerators_map(&self) -> HashMap<i32, String> {
733 match self.enumerators() {
734 Some(iter) => iter.collect(),
735 None => HashMap::new(),
736 }
737 }
738
739 pub fn from_id(id: u32) -> Option<Self> {
740 ControlId::try_from(id).ok()
741 }
742}
743
744impl PropertyId {
745 pub fn name(&self) -> String {
746 unsafe { CStr::from_ptr(libcamera_property_name_from_id(self.id())) }
747 .to_str()
748 .unwrap()
749 .into()
750 }
751}