1#[cfg(target_os = "linux")]
18mod internal {
19 use nokhwa_core::{
20 buffer::Buffer,
21 error::NokhwaError,
22 traits::CaptureBackendTrait,
23 types::{
24 ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo,
25 ControlValueDescription, ControlValueSetter, FrameFormat, KnownCameraControl,
26 KnownCameraControlFlag, RequestedFormat, RequestedFormatType, Resolution,
27 },
28 };
29 use std::{
30 borrow::Cow,
31 collections::HashMap,
32 io::{self, ErrorKind},
33 };
34 use v4l::v4l_sys::{
35 V4L2_CID_BACKLIGHT_COMPENSATION, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, V4L2_CID_EXPOSURE,
36 V4L2_CID_FOCUS_RELATIVE, V4L2_CID_GAIN, V4L2_CID_GAMMA, V4L2_CID_HUE,
37 V4L2_CID_IRIS_RELATIVE, V4L2_CID_PAN_RELATIVE, V4L2_CID_SATURATION, V4L2_CID_SHARPNESS,
38 V4L2_CID_TILT_RELATIVE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, V4L2_CID_ZOOM_RELATIVE,
39 };
40 use v4l::{
41 control::{Control, Flags, Type, Value},
42 frameinterval::FrameIntervalEnum,
43 framesize::FrameSizeEnum,
44 io::traits::{CaptureStream, Stream},
45 prelude::MmapStream,
46 video::{capture::Parameters, Capture},
47 Device, Format, FourCC,
48 };
49
50 #[allow(clippy::cast_possible_truncation)]
53 pub fn known_camera_control_to_id(ctrl: KnownCameraControl) -> u32 {
54 match ctrl {
55 KnownCameraControl::Brightness => V4L2_CID_BRIGHTNESS,
56 KnownCameraControl::Contrast => V4L2_CID_CONTRAST,
57 KnownCameraControl::Hue => V4L2_CID_HUE,
58 KnownCameraControl::Saturation => V4L2_CID_SATURATION,
59 KnownCameraControl::Sharpness => V4L2_CID_SHARPNESS,
60 KnownCameraControl::Gamma => V4L2_CID_GAMMA,
61 KnownCameraControl::WhiteBalance => V4L2_CID_WHITE_BALANCE_TEMPERATURE,
62 KnownCameraControl::BacklightComp => V4L2_CID_BACKLIGHT_COMPENSATION,
63 KnownCameraControl::Gain => V4L2_CID_GAIN,
64 KnownCameraControl::Pan => V4L2_CID_PAN_RELATIVE,
65 KnownCameraControl::Tilt => V4L2_CID_TILT_RELATIVE,
66 KnownCameraControl::Zoom => V4L2_CID_ZOOM_RELATIVE,
67 KnownCameraControl::Exposure => V4L2_CID_EXPOSURE,
68 KnownCameraControl::Iris => V4L2_CID_IRIS_RELATIVE,
69 KnownCameraControl::Focus => V4L2_CID_FOCUS_RELATIVE,
70 KnownCameraControl::Other(id) => id as u32,
71 }
72 }
73
74 #[allow(clippy::cast_lossless)]
77 pub fn id_to_known_camera_control(id: u32) -> KnownCameraControl {
78 match id {
79 V4L2_CID_BRIGHTNESS => KnownCameraControl::Brightness,
80 V4L2_CID_CONTRAST => KnownCameraControl::Contrast,
81 V4L2_CID_HUE => KnownCameraControl::Hue,
82 V4L2_CID_SATURATION => KnownCameraControl::Saturation,
83 V4L2_CID_SHARPNESS => KnownCameraControl::Sharpness,
84 V4L2_CID_GAMMA => KnownCameraControl::Gamma,
85 V4L2_CID_WHITE_BALANCE_TEMPERATURE => KnownCameraControl::WhiteBalance,
86 V4L2_CID_BACKLIGHT_COMPENSATION => KnownCameraControl::BacklightComp,
87 V4L2_CID_GAIN => KnownCameraControl::Gain,
88 V4L2_CID_PAN_RELATIVE => KnownCameraControl::Pan,
89 V4L2_CID_TILT_RELATIVE => KnownCameraControl::Tilt,
90 V4L2_CID_ZOOM_RELATIVE => KnownCameraControl::Zoom,
91 V4L2_CID_EXPOSURE => KnownCameraControl::Exposure,
92 V4L2_CID_IRIS_RELATIVE => KnownCameraControl::Iris,
93 V4L2_CID_FOCUS_RELATIVE => KnownCameraControl::Focus,
94 id => KnownCameraControl::Other(id as u128),
95 }
96 }
97
98 #[allow(clippy::unnecessary_wraps)]
100 #[allow(clippy::cast_possible_truncation)]
101 pub fn query() -> Result<Vec<CameraInfo>, NokhwaError> {
102 Ok({
103 let camera_info: Vec<CameraInfo> = v4l::context::enum_devices()
104 .iter()
105 .map(|node| {
106 CameraInfo::new(
107 &node
108 .name()
109 .unwrap_or(format!("{}", node.path().to_string_lossy())),
110 &format!("Video4Linux Device @ {}", node.path().to_string_lossy()),
111 "",
112 CameraIndex::Index(node.index() as u32),
113 )
114 })
115 .collect();
116 camera_info
117 })
118 }
119
120 type SharedDevice = std::sync::Arc<std::sync::Mutex<Device>>;
121 type WeakSharedDevice = std::sync::Weak<std::sync::Mutex<Device>>;
122
123 struct WeakSharedDeviceEntry {
124 device: WeakSharedDevice,
125 index: usize,
126 }
127
128 type SharedDeviceList = std::sync::OnceLock<std::sync::Mutex<Vec<WeakSharedDeviceEntry>>>;
129
130 static DEVICES: SharedDeviceList = std::sync::OnceLock::new();
138
139 fn cleanup_dropped_devices(devices: &mut Vec<WeakSharedDeviceEntry>) {
140 devices.retain(|entry| entry.device.strong_count() > 0);
141 }
142
143 fn new_shared_device(index: usize) -> Result<SharedDevice, NokhwaError> {
144 let mut devices = DEVICES
145 .get_or_init(|| std::sync::Mutex::new(Vec::new()))
146 .lock()
147 .map_err(|e| NokhwaError::InitializeError {
148 backend: ApiBackend::Video4Linux,
149 error: format!("Fail to lock global device list mutex: {}", e),
150 })?;
151
152 cleanup_dropped_devices(&mut devices);
155
156 if let Some(entry) = devices.iter().find(|entry| entry.index == index) {
157 if let Some(device) = entry.device.upgrade() {
158 return Ok(device);
159 }
160 }
161
162 cleanup_dropped_devices(&mut devices);
165
166 assert!(
168 devices.iter().find(|entry| entry.index == index).is_none(),
169 "Device {index} should not be in the list"
170 );
171
172 let device = match Device::new(index) {
175 Ok(dev) => dev,
176 Err(why) => {
177 return Err(NokhwaError::OpenDeviceError(
178 index.to_string(),
179 format!("V4L2 Error: {}", why),
180 ))
181 }
182 };
183
184 let device = std::sync::Arc::new(std::sync::Mutex::new(device));
185 devices.push(WeakSharedDeviceEntry {
186 device: std::sync::Arc::downgrade(&device),
187 index,
188 });
189
190 if devices.len() > 1 {
193 let indices: std::collections::HashSet<_> = devices.iter().map(|d| d.index).collect();
194 assert_eq!(
195 indices.len(),
196 devices.len(),
197 "Device list should not contain duplicate indexes"
198 );
199 }
200
201 Ok(device)
202 }
203
204 fn get_device_format(device: &Device) -> Result<CameraFormat, NokhwaError> {
205 match device.format() {
206 Ok(format) => {
207 let frame_format =
208 fourcc_to_frameformat(format.fourcc).ok_or(NokhwaError::GetPropertyError {
209 property: "FrameFormat".to_string(),
210 error: "unsupported".to_string(),
211 })?;
212
213 let fps = match device.params() {
214 Ok(params) => {
215 if params.interval.numerator != 1
216 || params.interval.denominator % params.interval.numerator != 0
217 {
218 return Err(NokhwaError::GetPropertyError {
219 property: "V4L2 FrameRate".to_string(),
220 error: format!(
221 "Framerate not whole number: {} / {}",
222 params.interval.denominator, params.interval.numerator
223 ),
224 });
225 }
226
227 if params.interval.numerator == 1 {
228 params.interval.denominator
229 } else {
230 params.interval.denominator / params.interval.numerator
231 }
232 }
233 Err(why) => {
234 return Err(NokhwaError::GetPropertyError {
235 property: "V4L2 FrameRate".to_string(),
236 error: why.to_string(),
237 })
238 }
239 };
240
241 Ok(CameraFormat::new(
242 Resolution::new(format.width, format.height),
243 frame_format,
244 fps,
245 ))
246 }
247 Err(why) => Err(NokhwaError::GetPropertyError {
248 property: "parameters".to_string(),
249 error: why.to_string(),
250 }),
251 }
252 }
253
254 pub struct V4LCaptureDevice<'a> {
259 camera_format: CameraFormat,
260 camera_info: CameraInfo,
261 device: SharedDevice,
262 stream_handle: Option<MmapStream<'a>>,
263 }
264
265 impl<'a> V4LCaptureDevice<'a> {
266 #[allow(clippy::too_many_lines)]
270 pub fn new(index: &CameraIndex, cam_fmt: RequestedFormat) -> Result<Self, NokhwaError> {
271 let index = index.clone();
272
273 let shared_device = new_shared_device(index.as_index()? as usize)?;
274 let device = shared_device
275 .lock()
276 .map_err(|e| NokhwaError::InitializeError {
277 backend: ApiBackend::Video4Linux,
278 error: format!("Fail to lock device mutex: {}", e),
279 })?;
280
281 let mut camera_formats = vec![];
284
285 let frame_formats = match device.enum_formats() {
286 Ok(formats) => {
287 let mut frame_format_vec = vec![];
288 formats
289 .iter()
290 .for_each(|fmt| frame_format_vec.push(fmt.fourcc));
291 frame_format_vec.dedup();
292 Ok(frame_format_vec)
293 }
294 Err(why) => Err(NokhwaError::GetPropertyError {
295 property: "FrameFormat".to_string(),
296 error: why.to_string(),
297 }),
298 }?;
299
300 for ff in frame_formats {
301 let framefmt = match fourcc_to_frameformat(ff) {
302 Some(s) => s,
303 None => continue,
304 };
305 let mut formats = device
307 .enum_framesizes(ff)
308 .map_err(|why| NokhwaError::GetPropertyError {
309 property: "ResolutionList".to_string(),
310 error: why.to_string(),
311 })?
312 .into_iter()
313 .flat_map(|x| {
314 match x.size {
315 FrameSizeEnum::Discrete(d) => {
316 [Resolution::new(d.width, d.height)].to_vec()
317 }
318 FrameSizeEnum::Stepwise(s) => (s.min_width..s.max_width)
320 .step_by(s.step_width as usize)
321 .zip((s.min_height..s.max_height).step_by(s.step_height as usize))
322 .map(|(x, y)| Resolution::new(x, y))
323 .collect(),
324 }
325 })
326 .flat_map(|res| {
327 device
328 .enum_frameintervals(ff, res.x(), res.y())
329 .unwrap_or_default()
330 .into_iter()
331 .flat_map(|x| match x.interval {
332 FrameIntervalEnum::Discrete(dis) => {
333 if dis.numerator == 1 {
334 vec![CameraFormat::new(
335 Resolution::new(x.width, x.height),
336 framefmt,
337 dis.denominator,
338 )]
339 } else {
340 vec![]
341 }
342 }
343 FrameIntervalEnum::Stepwise(step) => {
344 let mut intvec = vec![];
345 for fstep in (step.min.numerator..=step.max.numerator)
346 .step_by(step.step.numerator as usize)
347 {
348 if step.max.denominator != 1 || step.min.denominator != 1 {
349 intvec.push(CameraFormat::new(
350 Resolution::new(x.width, x.height),
351 framefmt,
352 fstep,
353 ));
354 }
355 }
356 intvec
357 }
358 })
359 })
360 .collect::<Vec<CameraFormat>>();
361 camera_formats.append(&mut formats);
362 }
363
364 let format = cam_fmt
365 .fulfill(&camera_formats)
366 .ok_or(NokhwaError::GetPropertyError {
367 property: "CameraFormat".to_string(),
368 error: "Failed to Fufill".to_string(),
369 })?;
370
371 let current_format = get_device_format(&device)?;
372
373 if current_format.width() != format.width()
374 || current_format.height() != format.height()
375 || current_format.format() != format.format()
376 {
377 if let Err(why) = device.set_format(&Format::new(
378 format.width(),
379 format.height(),
380 frameformat_to_fourcc(format.format()),
381 )) {
382 return Err(NokhwaError::SetPropertyError {
383 property: "Resolution, FrameFormat".to_string(),
384 value: format.to_string(),
385 error: why.to_string(),
386 });
387 }
388 }
389
390 if current_format.frame_rate() != format.frame_rate() {
391 if let Err(why) = device.set_params(&Parameters::with_fps(format.frame_rate())) {
392 return Err(NokhwaError::SetPropertyError {
393 property: "Frame rate".to_string(),
394 value: format.frame_rate().to_string(),
395 error: why.to_string(),
396 });
397 }
398 }
399
400 let device_caps = device
401 .query_caps()
402 .map_err(|why| NokhwaError::GetPropertyError {
403 property: "Device Capabilities".to_string(),
404 error: why.to_string(),
405 })?;
406
407 drop(device);
408
409 let mut v4l2 = V4LCaptureDevice {
410 camera_format: format,
411 camera_info: CameraInfo::new(
412 &device_caps.card,
413 &device_caps.driver,
414 &format!("{} {:?}", device_caps.bus, device_caps.version),
415 index,
416 ),
417 device: shared_device,
418 stream_handle: None,
419 };
420
421 v4l2.force_refresh_camera_format()?;
422 if v4l2.camera_format() != format {
423 return Err(NokhwaError::SetPropertyError {
424 property: "CameraFormat".to_string(),
425 value: String::new(),
426 error: "Not same/Rejected".to_string(),
427 });
428 }
429
430 Ok(v4l2)
431 }
432
433 #[deprecated(since = "0.10.0", note = "please use `new` instead.")]
437 #[allow(clippy::needless_pass_by_value)]
438 pub fn new_with(
439 index: CameraIndex,
440 width: u32,
441 height: u32,
442 fps: u32,
443 fourcc: FrameFormat,
444 ) -> Result<Self, NokhwaError> {
445 let camera_format = CameraFormat::new_from(width, height, fourcc, fps);
446 V4LCaptureDevice::new(
447 &index,
448 RequestedFormat::with_formats(
449 RequestedFormatType::Exact(camera_format),
450 vec![camera_format.format()].as_slice(),
451 ),
452 )
453 }
454
455 fn lock_device(&self) -> Result<std::sync::MutexGuard<'_, Device>, NokhwaError> {
456 self.device
457 .lock()
458 .map_err(|e| NokhwaError::GeneralError(format!("Failed to lock device: {}", e)))
459 }
460
461 fn get_resolution_list(&self, fourcc: FrameFormat) -> Result<Vec<Resolution>, NokhwaError> {
462 let format = frameformat_to_fourcc(fourcc);
463
464 match self.lock_device()?.enum_framesizes(format) {
465 Ok(frame_sizes) => {
466 let mut resolutions = vec![];
467 for frame_size in frame_sizes {
468 match frame_size.size {
469 FrameSizeEnum::Discrete(dis) => {
470 resolutions.push(Resolution::new(dis.width, dis.height));
471 }
472 FrameSizeEnum::Stepwise(step) => {
473 resolutions.push(Resolution::new(step.min_width, step.min_height));
474 resolutions.push(Resolution::new(step.max_width, step.max_height));
475 }
477 }
478 }
479 Ok(resolutions)
480 }
481 Err(why) => Err(NokhwaError::GetPropertyError {
482 property: "Resolutions".to_string(),
483 error: why.to_string(),
484 }),
485 }
486 }
487
488 pub fn force_refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
492 let camera_format = get_device_format(&*self.lock_device()?)?;
493 self.camera_format = camera_format;
494 Ok(())
495 }
496 }
497
498 impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
499 fn backend(&self) -> ApiBackend {
500 ApiBackend::Video4Linux
501 }
502
503 fn camera_info(&self) -> &CameraInfo {
504 &self.camera_info
505 }
506
507 fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
508 self.force_refresh_camera_format()
509 }
510
511 fn camera_format(&self) -> CameraFormat {
512 self.camera_format
513 }
514
515 fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
516 let device = self.lock_device()?;
517 let prev_format = match Capture::format(&*device) {
518 Ok(fmt) => fmt,
519 Err(why) => {
520 return Err(NokhwaError::GetPropertyError {
521 property: "Resolution, FrameFormat".to_string(),
522 error: why.to_string(),
523 })
524 }
525 };
526 let prev_fps = match Capture::params(&*device) {
527 Ok(fps) => fps,
528 Err(why) => {
529 return Err(NokhwaError::GetPropertyError {
530 property: "Frame rate".to_string(),
531 error: why.to_string(),
532 })
533 }
534 };
535
536 let v4l_fcc = match new_fmt.format() {
537 FrameFormat::MJPEG => FourCC::new(b"MJPG"),
538 FrameFormat::YUYV => FourCC::new(b"YUYV"),
539 FrameFormat::GRAY => FourCC::new(b"GRAY"),
540 FrameFormat::RAWRGB => FourCC::new(b"RGB3"),
541 FrameFormat::RAWBGR => FourCC::new(b"BGR3"),
542 FrameFormat::NV12 => FourCC::new(b"NV12"),
543 };
544
545 let format = Format::new(new_fmt.width(), new_fmt.height(), v4l_fcc);
546 let frame_rate = Parameters::with_fps(new_fmt.frame_rate());
547
548 if let Err(why) = Capture::set_format(&*device, &format) {
549 return Err(NokhwaError::SetPropertyError {
550 property: "Resolution, FrameFormat".to_string(),
551 value: format.to_string(),
552 error: why.to_string(),
553 });
554 }
555 if let Err(why) = Capture::set_params(&*device, &frame_rate) {
556 return Err(NokhwaError::SetPropertyError {
557 property: "Frame rate".to_string(),
558 value: frame_rate.to_string(),
559 error: why.to_string(),
560 });
561 }
562
563 drop(device);
564
565 if self.stream_handle.is_some() {
566 return match self.open_stream() {
567 Ok(_) => Ok(()),
568 Err(why) => {
569 let device = self.lock_device()?;
571 if let Err(why) = Capture::set_format(&*device, &prev_format) {
572 return Err(NokhwaError::SetPropertyError {
573 property: format!("Attempt undo due to stream acquisition failure with error {}. Resolution, FrameFormat", why),
574 value: prev_format.to_string(),
575 error: why.to_string(),
576 });
577 }
578 if let Err(why) = Capture::set_params(&*device, &prev_fps) {
579 return Err(NokhwaError::SetPropertyError {
580 property:
581 format!("Attempt undo due to stream acquisition failure with error {}. Frame rate", why),
582 value: prev_fps.to_string(),
583 error: why.to_string(),
584 });
585 }
586 Err(why)
587 }
588 };
589 }
590 self.camera_format = new_fmt;
591
592 self.force_refresh_camera_format()?;
593 if self.camera_format != new_fmt {
594 return Err(NokhwaError::SetPropertyError {
595 property: "CameraFormat".to_string(),
596 value: new_fmt.to_string(),
597 error: "Rejected".to_string(),
598 });
599 }
600
601 Ok(())
602 }
603
604 fn compatible_list_by_resolution(
605 &mut self,
606 fourcc: FrameFormat,
607 ) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
608 let resolutions = self.get_resolution_list(fourcc)?;
609 let format = frameformat_to_fourcc(fourcc);
610 let mut res_map = HashMap::new();
611 for res in resolutions {
612 let mut compatible_fps = vec![];
613 match self
614 .lock_device()?
615 .enum_frameintervals(format, res.width(), res.height())
616 {
617 Ok(intervals) => {
618 for interval in intervals {
619 match interval.interval {
620 FrameIntervalEnum::Discrete(dis) => {
621 compatible_fps.push(dis.denominator);
622 }
623 FrameIntervalEnum::Stepwise(step) => {
624 for fstep in (step.min.numerator..step.max.numerator)
625 .step_by(step.step.numerator as usize)
626 {
627 if step.max.denominator != 1 || step.min.denominator != 1 {
628 compatible_fps.push(fstep);
629 }
630 }
631 }
632 }
633 }
634 }
635 Err(why) => {
636 return Err(NokhwaError::GetPropertyError {
637 property: "Frame rate".to_string(),
638 error: why.to_string(),
639 })
640 }
641 }
642 res_map.insert(res, compatible_fps);
643 }
644 Ok(res_map)
645 }
646
647 fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
648 match self.lock_device()?.enum_formats() {
649 Ok(formats) => {
650 let mut frame_format_vec = vec![];
651 for format in formats {
652 match fourcc_to_frameformat(format.fourcc) {
653 Some(ff) => frame_format_vec.push(ff),
654 None => continue,
655 }
656 }
657 frame_format_vec.sort();
658 frame_format_vec.dedup();
659 Ok(frame_format_vec)
660 }
661 Err(why) => Err(NokhwaError::GetPropertyError {
662 property: "FrameFormat".to_string(),
663 error: why.to_string(),
664 }),
665 }
666 }
667
668 fn resolution(&self) -> Resolution {
669 self.camera_format.resolution()
670 }
671
672 fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
673 let mut new_fmt = self.camera_format;
674 new_fmt.set_resolution(new_res);
675 self.set_camera_format(new_fmt)
676 }
677
678 fn frame_rate(&self) -> u32 {
679 self.camera_format.frame_rate()
680 }
681
682 fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
683 let mut new_fmt = self.camera_format;
684 new_fmt.set_frame_rate(new_fps);
685 self.set_camera_format(new_fmt)
686 }
687
688 fn frame_format(&self) -> FrameFormat {
689 self.camera_format.format()
690 }
691
692 fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
693 let mut new_fmt = self.camera_format;
694 new_fmt.set_format(fourcc);
695 self.set_camera_format(new_fmt)
696 }
697
698 fn camera_control(
699 &self,
700 control: KnownCameraControl,
701 ) -> Result<CameraControl, NokhwaError> {
702 let controls = self.camera_controls()?;
703 for supported_control in controls {
704 if supported_control.control() == control {
705 return Ok(supported_control);
706 }
707 }
708 Err(NokhwaError::GetPropertyError {
709 property: control.to_string(),
710 error: "not found/not supported".to_string(),
711 })
712 }
713
714 #[allow(clippy::cast_possible_wrap)]
715 fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
716 let device = self.lock_device()?;
717 device
718 .query_controls()
719 .map_err(|why| NokhwaError::GetPropertyError {
720 property: "V4L2 Controls".to_string(),
721 error: why.to_string(),
722 })?
723 .into_iter()
724 .map(|desc| {
725 let id_as_kcc = id_to_known_camera_control(desc.id);
726 let ctrl_current = device.control(desc.id)?.value;
727
728 let ctrl_value_desc = match (desc.typ, ctrl_current) {
729 (
730 Type::Integer
731 | Type::Integer64
732 | Type::Menu
733 | Type::U8
734 | Type::U16
735 | Type::U32
736 | Type::IntegerMenu,
737 Value::Integer(current),
738 ) => ControlValueDescription::IntegerRange {
739 min: desc.minimum as i64,
740 max: desc.maximum,
741 value: current,
742 step: desc.step as i64,
743 default: desc.default,
744 },
745 (Type::Boolean, Value::Boolean(current)) => {
746 ControlValueDescription::Boolean {
747 value: current,
748 default: desc.default != 0,
749 }
750 }
751
752 (Type::String, Value::String(current)) => ControlValueDescription::String {
753 value: current,
754 default: None,
755 },
756 _ => {
757 return Err(io::Error::new(
758 ErrorKind::Unsupported,
759 "what is this?????? todo: support ig",
760 ))
761 }
762 };
763
764 let is_readonly = desc
765 .flags
766 .intersects(Flags::READ_ONLY)
767 .then_some(KnownCameraControlFlag::ReadOnly);
768 let is_writeonly = desc
769 .flags
770 .intersects(Flags::WRITE_ONLY)
771 .then_some(KnownCameraControlFlag::WriteOnly);
772 let is_disabled = desc
773 .flags
774 .intersects(Flags::DISABLED)
775 .then_some(KnownCameraControlFlag::Disabled);
776 let is_volatile = desc
777 .flags
778 .intersects(Flags::VOLATILE)
779 .then_some(KnownCameraControlFlag::Volatile);
780 let is_inactive = desc
781 .flags
782 .intersects(Flags::INACTIVE)
783 .then_some(KnownCameraControlFlag::Disabled);
784 let flags_vec = vec![
785 is_inactive,
786 is_readonly,
787 is_volatile,
788 is_disabled,
789 is_writeonly,
790 ]
791 .into_iter()
792 .filter(Option::is_some)
793 .collect::<Option<Vec<KnownCameraControlFlag>>>()
794 .unwrap_or_default();
795
796 Ok(CameraControl::new(
797 id_as_kcc,
798 desc.name,
799 ctrl_value_desc,
800 flags_vec,
801 !desc.flags.intersects(Flags::INACTIVE),
802 ))
803 })
804 .filter(Result::is_ok)
805 .collect::<Result<Vec<CameraControl>, io::Error>>()
806 .map_err(|x| NokhwaError::GetPropertyError {
807 property: "www".to_string(),
808 error: x.to_string(),
809 })
810 }
811
812 fn set_camera_control(
813 &mut self,
814 id: KnownCameraControl,
815 value: ControlValueSetter,
816 ) -> Result<(), NokhwaError> {
817 let conv_value = match value.clone() {
818 ControlValueSetter::None => Value::None,
819 ControlValueSetter::Integer(i) => Value::Integer(i),
820 ControlValueSetter::Boolean(b) => Value::Boolean(b),
821 ControlValueSetter::String(s) => Value::String(s),
822 ControlValueSetter::Bytes(b) => Value::CompoundU8(b),
823 v => {
824 return Err(NokhwaError::SetPropertyError {
825 property: id.to_string(),
826 value: v.to_string(),
827 error: "not supported".to_string(),
828 })
829 }
830 };
831 self.lock_device()?
832 .set_control(Control {
833 id: known_camera_control_to_id(id),
834 value: conv_value,
835 })
836 .map_err(|why| NokhwaError::SetPropertyError {
837 property: id.to_string(),
838 value: format!("{:?}", value),
839 error: why.to_string(),
840 })?;
841 let control = self.camera_control(id)?;
844 if control.value() != value {
845 return Err(NokhwaError::SetPropertyError {
846 property: id.to_string(),
847 value: format!("{:?}", value),
848 error: "Rejected".to_string(),
849 });
850 }
851 Ok(())
852 }
853
854 fn open_stream(&mut self) -> Result<(), NokhwaError> {
855 #[allow(unused_mut)]
857 let mut stream =
858 match MmapStream::new(&*self.lock_device()?, v4l::buffer::Type::VideoCapture) {
859 Ok(s) => s,
860 Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())),
861 };
862
863 #[cfg(feature = "no-arena-buffer")]
866 match stream.start() {
867 Ok(s) => s,
868 Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())),
869 }
870 self.stream_handle = Some(stream);
871 Ok(())
872 }
873
874 fn is_stream_open(&self) -> bool {
875 self.stream_handle.is_some()
876 }
877
878 fn frame(&mut self) -> Result<Buffer, NokhwaError> {
879 let cam_fmt = self.camera_format;
880 match &mut self.stream_handle {
881 Some(sh) => match sh.next() {
882 Ok((data, meta)) => {
883 let wall_ts = monotonic_to_wallclock(meta.timestamp);
884 Ok(Buffer::with_timestamp(
885 cam_fmt.resolution(),
886 data,
887 cam_fmt.format(),
888 wall_ts,
889 ))
890 }
891 Err(why) => Err(NokhwaError::ReadFrameError(why.to_string())),
892 },
893 None => Err(NokhwaError::ReadFrameError(
894 "Stream Not Started".to_string(),
895 )),
896 }
897 }
898
899 fn frame_raw(&mut self) -> Result<Cow<'_, [u8]>, NokhwaError> {
900 match &mut self.stream_handle {
901 Some(sh) => match sh.next() {
902 Ok((data, _)) => Ok(Cow::Borrowed(data)),
903 Err(why) => Err(NokhwaError::ReadFrameError(why.to_string())),
904 },
905 None => Err(NokhwaError::ReadFrameError(
906 "Stream Not Started".to_string(),
907 )),
908 }
909 }
910
911 fn stop_stream(&mut self) -> Result<(), NokhwaError> {
912 if self.stream_handle.is_some() {
913 self.stream_handle = None;
914 }
915 Ok(())
916 }
917 }
918
919 fn fourcc_to_frameformat(fourcc: FourCC) -> Option<FrameFormat> {
920 match fourcc.str().ok()? {
921 "YUYV" => Some(FrameFormat::YUYV),
922 "MJPG" => Some(FrameFormat::MJPEG),
923 "GRAY" => Some(FrameFormat::GRAY),
924 "RGB3" => Some(FrameFormat::RAWRGB),
925 "BGR3" => Some(FrameFormat::RAWBGR),
926 "NV12" => Some(FrameFormat::NV12),
927 _ => None,
928 }
929 }
930
931 fn frameformat_to_fourcc(fourcc: FrameFormat) -> FourCC {
932 match fourcc {
933 FrameFormat::MJPEG => FourCC::new(b"MJPG"),
934 FrameFormat::YUYV => FourCC::new(b"YUYV"),
935 FrameFormat::GRAY => FourCC::new(b"GRAY"),
936 FrameFormat::RAWRGB => FourCC::new(b"RGB3"),
937 FrameFormat::RAWBGR => FourCC::new(b"BGR3"),
938 FrameFormat::NV12 => FourCC::new(b"NV12"),
939 }
940 }
941
942 fn monotonic_to_wallclock(ts: v4l::Timestamp) -> Option<std::time::Duration> {
944 let frame_mono = std::time::Duration::from(ts);
945 if frame_mono.is_zero() {
946 return None;
947 }
948
949 let mut mono_now = libc::timespec {
950 tv_sec: 0,
951 tv_nsec: 0,
952 };
953 let mut wall_now = libc::timespec {
954 tv_sec: 0,
955 tv_nsec: 0,
956 };
957 unsafe {
959 libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut mono_now);
960 libc::clock_gettime(libc::CLOCK_REALTIME, &mut wall_now);
961 }
962 let mono_now =
963 std::time::Duration::new(mono_now.tv_sec as u64, mono_now.tv_nsec as u32);
964 let wall_now =
965 std::time::Duration::new(wall_now.tv_sec as u64, wall_now.tv_nsec as u32);
966
967 let frame_age = mono_now.checked_sub(frame_mono)?;
969 wall_now.checked_sub(frame_age)
970 }
971}
972
973#[cfg(not(target_os = "linux"))]
974mod internal {
975 use nokhwa_core::buffer::Buffer;
976 use nokhwa_core::error::NokhwaError;
977 use nokhwa_core::traits::CaptureBackendTrait;
978 use nokhwa_core::types::{
979 ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
980 FrameFormat, KnownCameraControl, RequestedFormat, Resolution,
981 };
982 use std::borrow::Cow;
983 use std::collections::HashMap;
984 use std::marker::PhantomData;
985
986 #[allow(clippy::cast_possible_truncation)]
989 pub fn known_camera_control_to_id(_ctrl: KnownCameraControl) -> u32 {
990 0
991 }
992
993 #[allow(clippy::cast_lossless)]
996 pub fn id_to_known_camera_control(id: u32) -> KnownCameraControl {
997 KnownCameraControl::Other(id as u128)
998 }
999
1000 pub struct V4LCaptureDevice<'a> {
1005 __holder: PhantomData<&'a str>,
1006 }
1007
1008 #[allow(unused_variables)]
1009 impl<'a> V4LCaptureDevice<'a> {
1010 #[allow(clippy::too_many_lines)]
1014 pub fn new(index: &CameraIndex, cam_fmt: RequestedFormat) -> Result<Self, NokhwaError> {
1015 Err(NokhwaError::NotImplementedError(
1016 "V4L2 only on Linux".to_string(),
1017 ))
1018 }
1019
1020 #[deprecated(since = "0.10.0", note = "please use `new` instead.")]
1024 pub fn new_with(
1025 index: CameraIndex,
1026 width: u32,
1027 height: u32,
1028 fps: u32,
1029 fourcc: FrameFormat,
1030 ) -> Result<Self, NokhwaError> {
1031 Err(NokhwaError::NotImplementedError(
1032 "V4L2 only on Linux".to_string(),
1033 ))
1034 }
1035
1036 pub fn force_refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
1040 Err(NokhwaError::NotImplementedError(
1041 "V4L2 only on Linux".to_string(),
1042 ))
1043 }
1044 }
1045
1046 #[allow(unused_variables)]
1047 impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
1048 fn backend(&self) -> ApiBackend {
1049 ApiBackend::Video4Linux
1050 }
1051
1052 fn camera_info(&self) -> &CameraInfo {
1053 todo!()
1054 }
1055
1056 fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
1057 todo!()
1058 }
1059
1060 fn camera_format(&self) -> CameraFormat {
1061 todo!()
1062 }
1063
1064 fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
1065 todo!()
1066 }
1067
1068 fn compatible_list_by_resolution(
1069 &mut self,
1070 fourcc: FrameFormat,
1071 ) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
1072 todo!()
1073 }
1074
1075 fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
1076 todo!()
1077 }
1078
1079 fn resolution(&self) -> Resolution {
1080 todo!()
1081 }
1082
1083 fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
1084 todo!()
1085 }
1086
1087 fn frame_rate(&self) -> u32 {
1088 todo!()
1089 }
1090
1091 fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
1092 todo!()
1093 }
1094
1095 fn frame_format(&self) -> FrameFormat {
1096 todo!()
1097 }
1098
1099 fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
1100 todo!()
1101 }
1102
1103 fn camera_control(
1104 &self,
1105 control: KnownCameraControl,
1106 ) -> Result<CameraControl, NokhwaError> {
1107 todo!()
1108 }
1109
1110 fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
1111 todo!()
1112 }
1113
1114 fn set_camera_control(
1115 &mut self,
1116 id: KnownCameraControl,
1117 value: ControlValueSetter,
1118 ) -> Result<(), NokhwaError> {
1119 todo!()
1120 }
1121
1122 fn open_stream(&mut self) -> Result<(), NokhwaError> {
1123 todo!()
1124 }
1125
1126 fn is_stream_open(&self) -> bool {
1127 todo!()
1128 }
1129
1130 fn frame(&mut self) -> Result<Buffer, NokhwaError> {
1131 todo!()
1132 }
1133
1134 fn frame_raw(&mut self) -> Result<Cow<'_, [u8]>, NokhwaError> {
1135 todo!()
1136 }
1137
1138 fn stop_stream(&mut self) -> Result<(), NokhwaError> {
1139 todo!()
1140 }
1141 }
1142}
1143
1144pub use internal::*;