Skip to main content

nokhwa_bindings_linux/
lib.rs

1/*
2 * Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#[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    /// Attempts to convert a [`KnownCameraControl`] into a V4L2 Control ID.
51    /// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`)
52    #[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    /// Attempts to convert a [`u32`] V4L2 Control ID into a [`KnownCameraControl`]
75    /// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`)
76    #[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    /// query v4l2 cameras
99    #[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    /// Global array that keep track of every Device that are currently open.
131    /// This is used to open multiple handle to the same device.
132    /// This is a workaround for the fact that the v4l2 backend does not support multiple handles to the same device.
133    /// This replicate behavior of MF backend.
134    /// The reference is a reference of Weak<Mutex<Device>>, so that the Device can be dropped when the last handle is closed.
135    /// This list might need some cleanup because it will accumulate every device that is opened, this should not be a problem because the list should not grow too much.
136    /// The list is also protected by a mutex, so it should be thread safe.
137    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        // do some cleanup, this will avoid here memory to grow forever
153        // if for some reason someone has tons of camera plugged in
154        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 a second, the device we are interested might have been dropped during before upgrade call
163        // For this point on we are assured that the device is not in the list
164        cleanup_dropped_devices(&mut devices);
165
166        // Let's be extra sure, this code should never panic, but maybe will help catch some race condition
167        assert!(
168            devices.iter().find(|entry| entry.index == index).is_none(),
169            "Device {index} should not be in the list"
170        );
171
172        // Now we can open the device, and never run into a busy io error,
173        // as long as the device isn't opened by other programs.
174        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        // Last check to be sure that every devices have a unique index
191        // and that the data isn't corrupted
192        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    /// The backend struct that interfaces with V4L2.
255    /// To see what this does, please see [`CaptureBackendTrait`].
256    /// # Quirks
257    /// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_frame_rate()`](CaptureBackendTrait::set_frame_rate), or [`set_frame_format()`](CaptureBackendTrait::set_frame_format) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
258    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        /// Creates a new capture device using the `V4L2` backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery.
267        /// # Errors
268        /// This function will error if the camera is currently busy or if `V4L2` can't read device information.
269        #[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            // get all formats
282            // get all fcc
283            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                // i write unmaintainable blobs of code because i am so cute uwu~~
306                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                            // we step over each step, getting a new resolution.
319                            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        /// Create a new `V4L2` Camera with desired settings. This may or may not work.
434        /// # Errors
435        /// This function will error if the camera is currently busy or if `V4L2` can't read device information.
436        #[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                                // TODO: Respect step size
476                            }
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        /// Force refreshes the inner [`CameraFormat`] state.
489        /// # Errors
490        /// If the internal representation in the driver is invalid, this will error.
491        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                        // undo
570                        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            // verify
842
843            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            // Disable mut warning, since mut is only required when not using arena buffers
856            #[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            // Explicitly start now, or won't work with the RPi. As a consequence, buffers will only be used as required.
864            // WARNING: This will cause drop of half of the frames
865            #[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    /// Convert a V4L2 CLOCK_MONOTONIC timestamp to a wallclock Duration since UNIX_EPOCH.
943    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        // SAFETY: passing valid pointers to kernel clock_gettime
958        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        // frame_age = how long ago the frame was captured (monotonic delta)
968        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    /// Attempts to convert a [`KnownCameraControl`] into a V4L2 Control ID.
987    /// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`)
988    #[allow(clippy::cast_possible_truncation)]
989    pub fn known_camera_control_to_id(_ctrl: KnownCameraControl) -> u32 {
990        0
991    }
992
993    /// Attempts to convert a [`u32`] V4L2 Control ID into a [`KnownCameraControl`]
994    /// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`)
995    #[allow(clippy::cast_lossless)]
996    pub fn id_to_known_camera_control(id: u32) -> KnownCameraControl {
997        KnownCameraControl::Other(id as u128)
998    }
999
1000    /// The backend struct that interfaces with V4L2.
1001    /// To see what this does, please see [`CaptureBackendTrait`].
1002    /// # Quirks
1003    /// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_frame_rate()`](CaptureBackendTrait::set_frame_rate), or [`set_frame_format()`](CaptureBackendTrait::set_frame_format) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
1004    pub struct V4LCaptureDevice<'a> {
1005        __holder: PhantomData<&'a str>,
1006    }
1007
1008    #[allow(unused_variables)]
1009    impl<'a> V4LCaptureDevice<'a> {
1010        /// Creates a new capture device using the `V4L2` backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery.
1011        /// # Errors
1012        /// This function will error if the camera is currently busy or if `V4L2` can't read device information.
1013        #[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        /// Create a new `V4L2` Camera with desired settings. This may or may not work.
1021        /// # Errors
1022        /// This function will error if the camera is currently busy or if `V4L2` can't read device information.
1023        #[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        /// Force refreshes the inner [`CameraFormat`] state.
1037        /// # Errors
1038        /// If the internal representation in the driver is invalid, this will error.
1039        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::*;