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