diff --git a/Cargo.toml b/Cargo.toml index f90ebaf..0f67f78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/l1npengtul/nokhwa" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-core", "examples/*"] +members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "examples/*"] exclude = ["examples/jscam"] [lib] @@ -21,10 +21,10 @@ crate-type = ["cdylib", "rlib"] default = ["flume", "decoding"] serialize = ["serde", "nokhwa-core/serialize"] decoding = ["nokhwa-core/mjpeg"] -input-native = ["input-avfoundation", "input-v4l", "input-msmf"] -input-v4l = ["v4l"] -input-msmf = ["nokhwa-bindings-windows"] input-avfoundation = ["nokhwa-bindings-macos"] +input-msmf = ["nokhwa-bindings-windows"] +input-v4l = ["nokhwa-bindings-linux"] +input-native = ["input-avfoundation", "input-v4l", "input-msmf"] # Re-enable it once soundness has been proven + mozjpeg is updated to 0.9.x # input-uvc = ["uvc", "uvc/vendor", "usb_enumeration", "lazy_static"] input-opencv = ["opencv", "opencv/rgb", "rgb"] @@ -33,7 +33,7 @@ output-wgpu = ["wgpu", "nokhwa-core/wgpu-types"] #output-wasm = ["input-jscam"] output-threaded = [] small-wasm = [] -docs-only = ["input-v4l", "input-opencv", "input-msmf", "input-avfoundation", "input-jscam","output-wgpu", "output-threaded"] +docs-only = ["input-native", "input-opencv", "input-jscam","output-wgpu", "output-threaded", "serialize"] docs-nolink = ["opencv/docs-only"] docs-features = [] test-fail-warning = [] @@ -90,6 +90,11 @@ version = "0.2.0-rc.1" path = "nokhwa-bindings-macos" optional = true +[dependencies.nokhwa-bindings-linux] +version = "0.1.0" +path = "nokhwa-bindings-linux" +optional = true + [dependencies.regex] version = "1.4.6" optional = true diff --git a/nokhwa-bindings-windows/Cargo.toml b/nokhwa-bindings-windows/Cargo.toml index dbec1da..9c20210 100644 --- a/nokhwa-bindings-windows/Cargo.toml +++ b/nokhwa-bindings-windows/Cargo.toml @@ -17,6 +17,6 @@ lazy_static = "1.4.0" version = "0.1.0-rc.2" path = "../nokhwa-core" -[dependencies.windows] +[target.'cfg(target_os="windows")'.dependencies.windows] version = "0.43.0" features = ["Win32_Media_MediaFoundation", "Win32_System_Com", "Win32_Foundation", "Win32_Media_DirectShow", "Win32_Media", "Win32", "Win32_Media_KernelStreaming"] diff --git a/src/backends/capture/mod.rs b/src/backends/capture/mod.rs index fe4b491..67d26ab 100644 --- a/src/backends/capture/mod.rs +++ b/src/backends/capture/mod.rs @@ -14,12 +14,9 @@ * limitations under the License. */ -#[cfg(all(feature = "input-v4l", target_os = "linux"))] -// I'm too lazy to set up a skeleton facade for V4L so here it will stay -mod v4l2_backend; #[cfg(all(feature = "input-v4l", target_os = "linux"))] #[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-v4l")))] -pub use v4l2_backend::V4LCaptureDevice; +pub use nokhwa_bindings_linux::V4LCaptureDevice; #[cfg(any( all(feature = "input-msmf", target_os = "windows"), all(feature = "docs-only", feature = "docs-nolink", feature = "input-msmf") diff --git a/src/backends/capture/v4l2_backend.rs b/src/backends/capture/v4l2_backend.rs deleted file mode 100644 index cbed58f..0000000 --- a/src/backends/capture/v4l2_backend.rs +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright 2022 l1npengtul / The Nokhwa Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use nokhwa_core::{ - buffer::Buffer, - error::NokhwaError, - traits::CaptureBackendTrait, - types::{ - ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueDescription, - ControlValueSetter, FrameFormat, KnownCameraControl, KnownCameraControlFlag, - RequestedFormat, RequestedFormatType, Resolution, - }, -}; -use std::{ - borrow::Cow, - collections::HashMap, - io::{self, ErrorKind}, -}; -use v4l::{ - control::{Control, Flags, Type, Value}, - frameinterval::FrameIntervalEnum, - framesize::FrameSizeEnum, - io::traits::CaptureStream, - prelude::MmapStream, - video::{capture::Parameters, Capture}, - Device, Format, FourCC, -}; - -/// Attempts to convert a [`KnownCameraControl`] into a V4L2 Control ID. -/// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`) -#[allow(clippy::cast_possible_truncation)] -pub fn known_camera_control_to_id(ctrl: KnownCameraControl) -> u32 { - match ctrl { - KnownCameraControl::Brightness => 9_963_776, - KnownCameraControl::Contrast => 9_963_777, - KnownCameraControl::Hue => 9_963_779, - KnownCameraControl::Saturation => 9_963_778, - KnownCameraControl::Sharpness => 9_963_803, - KnownCameraControl::Gamma => 9_963_792, - KnownCameraControl::WhiteBalance => 9_963_802, - KnownCameraControl::BacklightComp => 9_963_804, - KnownCameraControl::Gain => 9_963_795, - KnownCameraControl::Pan => 10_094_852, - KnownCameraControl::Tilt => 100_948_530, - KnownCameraControl::Zoom => 10_094_862, - KnownCameraControl::Exposure => 10_094_850, - KnownCameraControl::Iris => 10_094_866, - KnownCameraControl::Focus => 10_094_859, - KnownCameraControl::Other(id) => id as u32, - } -} - -/// Attempts to convert a [`u32`] V4L2 Control ID into a [`KnownCameraControl`] -/// If the associated control is not found, this will return `None` (`ColorEnable`, `Roll`) -#[allow(clippy::cast_lossless)] -pub fn id_to_known_camera_control(id: u32) -> KnownCameraControl { - match id { - 9_963_776 => KnownCameraControl::Brightness, - 9_963_777 => KnownCameraControl::Contrast, - 9_963_779 => KnownCameraControl::Hue, - 9_963_778 => KnownCameraControl::Saturation, - 9_963_803 => KnownCameraControl::Sharpness, - 9_963_792 => KnownCameraControl::Gamma, - 9_963_802 => KnownCameraControl::WhiteBalance, - 9_963_804 => KnownCameraControl::BacklightComp, - 9_963_795 => KnownCameraControl::Gain, - 10_094_852 => KnownCameraControl::Pan, - 100_948_530 => KnownCameraControl::Tilt, - 10_094_862 => KnownCameraControl::Zoom, - 10_094_850 => KnownCameraControl::Exposure, - 10_094_866 => KnownCameraControl::Iris, - 10_094_859 => KnownCameraControl::Focus, - id => KnownCameraControl::Other(id as u128), - } -} - -/// The backend struct that interfaces with V4L2. -/// To see what this does, please see [`CaptureBackendTrait`]. -/// # Quirks -/// - 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). -#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-v4l")))] -pub struct V4LCaptureDevice<'a> { - camera_format: CameraFormat, - camera_info: CameraInfo, - device: Device, - stream_handle: Option>, -} - -impl<'a> V4LCaptureDevice<'a> { - /// Creates a new capture device using the `V4L2` backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery. - /// # Errors - /// This function will error if the camera is currently busy or if `V4L2` can't read device information. - #[allow(clippy::too_many_lines)] - pub fn new(index: &CameraIndex, cam_fmt: RequestedFormat) -> Result { - let index = index.clone(); - let device = match Device::new(index.as_index()? as usize) { - Ok(dev) => dev, - Err(why) => { - return Err(NokhwaError::OpenDeviceError( - index.to_string(), - format!("V4L2 Error: {}", why), - )) - } - }; - - // get all formats - // get all fcc - let mut camera_formats = vec![]; - - let frame_formats = match device.enum_formats() { - Ok(formats) => { - let mut frame_format_vec = vec![]; - formats - .iter() - .for_each(|fmt| frame_format_vec.push(fmt.fourcc)); - frame_format_vec.dedup(); - Ok(frame_format_vec) - } - Err(why) => Err(NokhwaError::GetPropertyError { - property: "FrameFormat".to_string(), - error: why.to_string(), - }), - }?; - - for ff in frame_formats { - let framefmt = match fourcc_to_frameformat(ff) { - Some(s) => s, - None => continue, - }; - // i write unmaintainable blobs of code because i am so cute uwu~~ - let mut formats = device - .enum_framesizes(ff) - .map_err(|why| NokhwaError::GetPropertyError { - property: "ResolutionList".to_string(), - error: why.to_string(), - })? - .into_iter() - .flat_map(|x| { - match x.size { - FrameSizeEnum::Discrete(d) => [Resolution::new(d.width, d.height)].to_vec(), - // we step over each step, getting a new resolution. - FrameSizeEnum::Stepwise(s) => (s.min_width..s.max_width) - .step_by(s.step_width as usize) - .zip((s.min_height..s.max_height).step_by(s.step_height as usize)) - .map(|(x, y)| Resolution::new(x, y)) - .collect(), - } - }) - .flat_map(|res| { - device - .enum_frameintervals(ff, res.x(), res.y()) - .unwrap_or_default() - .into_iter() - .flat_map(|x| match x.interval { - FrameIntervalEnum::Discrete(dis) => { - if dis.numerator == 1 { - vec![CameraFormat::new( - Resolution::new(x.width, x.height), - framefmt, - dis.denominator, - )] - } else { - vec![] - } - } - FrameIntervalEnum::Stepwise(step) => { - let mut intvec = vec![]; - for fstep in (step.min.numerator..step.max.numerator) - .step_by(step.step.numerator as usize) - { - if step.max.denominator != 1 || step.min.denominator != 1 { - intvec.push(CameraFormat::new( - Resolution::new(x.width, x.height), - framefmt, - fstep, - )); - } - } - intvec - } - }) - }) - .collect::>(); - camera_formats.append(&mut formats); - } - - let format = cam_fmt - .fulfill(&camera_formats) - .ok_or(NokhwaError::GetPropertyError { - property: "CameraFormat".to_string(), - error: "Failed to Fufill".to_string(), - })?; - - if let Err(why) = device.set_format(&Format::new( - format.width(), - format.height(), - frameformat_to_fourcc(format.format()), - )) { - return Err(NokhwaError::SetPropertyError { - property: "Resolution, FrameFormat".to_string(), - value: format.to_string(), - error: why.to_string(), - }); - } - if let Err(why) = device.set_params(&Parameters::with_fps(format.frame_rate())) { - return Err(NokhwaError::SetPropertyError { - property: "Frame rate".to_string(), - value: format.frame_rate().to_string(), - error: why.to_string(), - }); - } - - let device_caps = device - .query_caps() - .map_err(|why| NokhwaError::GetPropertyError { - property: "Device Capabilities".to_string(), - error: why.to_string(), - })?; - - let mut v4l2 = V4LCaptureDevice { - camera_format: format, - camera_info: CameraInfo::new( - &device_caps.card, - &device_caps.driver, - &format!("{} {:?}", device_caps.bus, device_caps.version), - index, - ), - device, - stream_handle: None, - }; - - v4l2.force_refresh_camera_format()?; - if v4l2.camera_format() != format { - return Err(NokhwaError::SetPropertyError { - property: "CameraFormat".to_string(), - value: String::new(), - error: "Not same/Rejected".to_string(), - }); - } - - Ok(v4l2) - } - - /// Create a new `V4L2` Camera with desired settings. This may or may not work. - /// # Errors - /// This function will error if the camera is currently busy or if `V4L2` can't read device information. - #[deprecated(since = "0.10.0", note = "please use `new` instead.")] - #[allow(clippy::needless_pass_by_value)] - pub fn new_with( - index: CameraIndex, - width: u32, - height: u32, - fps: u32, - fourcc: FrameFormat, - ) -> Result { - let camera_format = CameraFormat::new_from(width, height, fourcc, fps); - V4LCaptureDevice::new( - &index, - RequestedFormat::with_formats( - RequestedFormatType::Exact(camera_format), - vec![camera_format.format()].as_slice(), - ), - ) - } - - fn get_resolution_list(&self, fourcc: FrameFormat) -> Result, NokhwaError> { - let format = frameformat_to_fourcc(fourcc); - - // match Capture::enum_framesizes(&self.device, format) { - match self.device.enum_framesizes(format) { - Ok(frame_sizes) => { - let mut resolutions = vec![]; - for frame_size in frame_sizes { - match frame_size.size { - FrameSizeEnum::Discrete(dis) => { - resolutions.push(Resolution::new(dis.width, dis.height)); - } - FrameSizeEnum::Stepwise(step) => { - resolutions.push(Resolution::new(step.min_width, step.min_height)); - resolutions.push(Resolution::new(step.max_width, step.max_height)); - // TODO: Respect step size - } - } - } - Ok(resolutions) - } - Err(why) => Err(NokhwaError::GetPropertyError { - property: "Resolutions".to_string(), - error: why.to_string(), - }), - } - } - - /// Get the inner device (immutable) for e.g. Controls - /// apps bloodtests contact css images index index.html injectionsupplies transfem transmasc - #[allow(clippy::must_use_candidate)] - pub fn inner_device(&self) -> &Device { - &self.device - } - - /// Get the inner device (mutable) for e.g. Controls - pub fn inner_device_mut(&mut self) -> &mut Device { - &mut self.device - } - - /// Force refreshes the inner [`CameraFormat`] state. - /// # Errors - /// If the internal representation in the driver is invalid, this will error. - pub fn force_refresh_camera_format(&mut self) -> Result<(), NokhwaError> { - match self.device.format() { - Ok(format) => { - let frame_format = - fourcc_to_frameformat(format.fourcc).ok_or(NokhwaError::GetPropertyError { - property: "FrameFormat".to_string(), - error: "unsupported".to_string(), - })?; - - let fps = match self.device.params() { - Ok(params) => { - if params.interval.numerator != 1 - || params.interval.denominator % params.interval.numerator != 0 - { - return Err(NokhwaError::GetPropertyError { - property: "V4L2 FrameRate".to_string(), - error: format!( - "Framerate not whole number: {} / {}", - params.interval.denominator, params.interval.numerator - ), - }); - } - - if params.interval.numerator == 1 { - params.interval.denominator - } else { - params.interval.denominator / params.interval.numerator - } - } - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "V4L2 FrameRate".to_string(), - error: why.to_string(), - }) - } - }; - - self.camera_format = CameraFormat::new( - Resolution::new(format.width, format.height), - frame_format, - fps, - ); - Ok(()) - } - Err(why) => Err(NokhwaError::GetPropertyError { - property: "parameters".to_string(), - error: why.to_string(), - }), - } - } -} - -impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> { - fn backend(&self) -> ApiBackend { - ApiBackend::Video4Linux - } - - fn camera_info(&self) -> &CameraInfo { - &self.camera_info - } - - fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> { - self.force_refresh_camera_format() - } - - fn camera_format(&self) -> CameraFormat { - self.camera_format - } - - fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> { - let prev_format = match Capture::format(&self.device) { - Ok(fmt) => fmt, - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "Resolution, FrameFormat".to_string(), - error: why.to_string(), - }) - } - }; - let prev_fps = match Capture::params(&self.device) { - Ok(fps) => fps, - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "Frame rate".to_string(), - error: why.to_string(), - }) - } - }; - - let v4l_fcc = match new_fmt.format() { - FrameFormat::MJPEG => FourCC::new(b"MJPG"), - FrameFormat::YUYV => FourCC::new(b"YUYV"), - FrameFormat::GRAY => FourCC::new(b"GRAY"), - FrameFormat::RAWRGB => FourCC::new(b"RGB3"), - FrameFormat::NV12 => FourCC::new(b"NV12"), - }; - - let format = Format::new(new_fmt.width(), new_fmt.height(), v4l_fcc); - let frame_rate = Parameters::with_fps(new_fmt.frame_rate()); - - if let Err(why) = Capture::set_format(&self.device, &format) { - return Err(NokhwaError::SetPropertyError { - property: "Resolution, FrameFormat".to_string(), - value: format.to_string(), - error: why.to_string(), - }); - } - if let Err(why) = Capture::set_params(&self.device, &frame_rate) { - return Err(NokhwaError::SetPropertyError { - property: "Frame rate".to_string(), - value: frame_rate.to_string(), - error: why.to_string(), - }); - } - - if self.stream_handle.is_some() { - return match self.open_stream() { - Ok(_) => Ok(()), - Err(why) => { - // undo - if let Err(why) = Capture::set_format(&self.device, &prev_format) { - return Err(NokhwaError::SetPropertyError { - property: format!("Attempt undo due to stream acquisition failure with error {}. Resolution, FrameFormat", why), - value: prev_format.to_string(), - error: why.to_string(), - }); - } - if let Err(why) = Capture::set_params(&self.device, &prev_fps) { - return Err(NokhwaError::SetPropertyError { - property: - format!("Attempt undo due to stream acquisition failure with error {}. Frame rate", why), - value: prev_fps.to_string(), - error: why.to_string(), - }); - } - Err(why) - } - }; - } - self.camera_format = new_fmt; - - self.force_refresh_camera_format()?; - if self.camera_format != new_fmt { - return Err(NokhwaError::SetPropertyError { - property: "CameraFormat".to_string(), - value: new_fmt.to_string(), - error: "Rejected".to_string(), - }); - } - - Ok(()) - } - - fn compatible_list_by_resolution( - &mut self, - fourcc: FrameFormat, - ) -> Result>, NokhwaError> { - let resolutions = self.get_resolution_list(fourcc)?; - let format = frameformat_to_fourcc(fourcc); - let mut res_map = HashMap::new(); - for res in resolutions { - let mut compatible_fps = vec![]; - match self - .device - .enum_frameintervals(format, res.width(), res.height()) - { - Ok(intervals) => { - for interval in intervals { - match interval.interval { - FrameIntervalEnum::Discrete(dis) => { - compatible_fps.push(dis.denominator); - } - FrameIntervalEnum::Stepwise(step) => { - for fstep in (step.min.numerator..step.max.numerator) - .step_by(step.step.numerator as usize) - { - if step.max.denominator != 1 || step.min.denominator != 1 { - compatible_fps.push(fstep); - } - } - } - } - } - } - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "Frame rate".to_string(), - error: why.to_string(), - }) - } - } - res_map.insert(res, compatible_fps); - } - Ok(res_map) - } - - fn compatible_fourcc(&mut self) -> Result, NokhwaError> { - match self.device.enum_formats() { - Ok(formats) => { - let mut frame_format_vec = vec![]; - for format in formats { - match fourcc_to_frameformat(format.fourcc) { - Some(ff) => frame_format_vec.push(ff), - None => continue, - } - } - frame_format_vec.sort(); - frame_format_vec.dedup(); - Ok(frame_format_vec) - } - Err(why) => Err(NokhwaError::GetPropertyError { - property: "FrameFormat".to_string(), - error: why.to_string(), - }), - } - } - - fn resolution(&self) -> Resolution { - self.camera_format.resolution() - } - - fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> { - let mut new_fmt = self.camera_format; - new_fmt.set_resolution(new_res); - self.set_camera_format(new_fmt) - } - - fn frame_rate(&self) -> u32 { - self.camera_format.frame_rate() - } - - fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> { - let mut new_fmt = self.camera_format; - new_fmt.set_frame_rate(new_fps); - self.set_camera_format(new_fmt) - } - - fn frame_format(&self) -> FrameFormat { - self.camera_format.format() - } - - fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> { - let mut new_fmt = self.camera_format; - new_fmt.set_format(fourcc); - self.set_camera_format(new_fmt) - } - - fn camera_control(&self, control: KnownCameraControl) -> Result { - let controls = self.camera_controls()?; - for supported_control in controls { - if supported_control.control() == control { - return Ok(supported_control); - } - } - Err(NokhwaError::GetPropertyError { - property: control.to_string(), - error: "not found/not supported".to_string(), - }) - } - - #[allow(clippy::cast_possible_wrap)] - fn camera_controls(&self) -> Result, NokhwaError> { - self.device - .query_controls() - .map_err(|why| NokhwaError::GetPropertyError { - property: "V4L2 Controls".to_string(), - error: why.to_string(), - })? - .into_iter() - .map(|desc| { - let id_as_kcc = id_to_known_camera_control(desc.id); - let ctrl_current = self.device.control(desc.id)?.value; - - let ctrl_value_desc = match (desc.typ, ctrl_current) { - ( - Type::Integer - | Type::Integer64 - | Type::Menu - | Type::U8 - | Type::U16 - | Type::U32 - | Type::IntegerMenu, - Value::Integer(current), - ) => ControlValueDescription::IntegerRange { - min: desc.minimum as i64, - max: desc.maximum, - value: current, - step: desc.step as i64, - default: desc.default, - }, - (Type::Boolean, Value::Boolean(current)) => ControlValueDescription::Boolean { - value: current, - default: desc.default != 0, - }, - - (Type::String, Value::String(current)) => ControlValueDescription::String { - value: current, - default: None, - }, - _ => { - return Err(io::Error::new( - ErrorKind::Unsupported, - "what is this?????? todo: support ig", - )) - } - }; - - let is_readonly = desc - .flags - .intersects(Flags::READ_ONLY) - .then_some(KnownCameraControlFlag::ReadOnly); - let is_writeonly = desc - .flags - .intersects(Flags::WRITE_ONLY) - .then_some(KnownCameraControlFlag::WriteOnly); - let is_disabled = desc - .flags - .intersects(Flags::DISABLED) - .then_some(KnownCameraControlFlag::Disabled); - let is_volatile = desc - .flags - .intersects(Flags::VOLATILE) - .then_some(KnownCameraControlFlag::Volatile); - let is_inactive = desc - .flags - .intersects(Flags::INACTIVE) - .then_some(KnownCameraControlFlag::Disabled); - let flags_vec = vec![ - is_inactive, - is_readonly, - is_volatile, - is_disabled, - is_writeonly, - ] - .into_iter() - .filter(Option::is_some) - .collect::>>() - .unwrap_or_default(); - - Ok(CameraControl::new( - id_as_kcc, - desc.name, - ctrl_value_desc, - flags_vec, - !desc.flags.intersects(Flags::INACTIVE), - )) - }) - .filter(Result::is_ok) - .collect::, io::Error>>() - .map_err(|x| NokhwaError::GetPropertyError { - property: "www".to_string(), - error: x.to_string(), - }) - } - - fn set_camera_control( - &mut self, - id: KnownCameraControl, - value: ControlValueSetter, - ) -> Result<(), NokhwaError> { - let conv_value = match value.clone() { - ControlValueSetter::None => Value::None, - ControlValueSetter::Integer(i) => Value::Integer(i), - ControlValueSetter::Boolean(b) => Value::Boolean(b), - ControlValueSetter::String(s) => Value::String(s), - ControlValueSetter::Bytes(b) => Value::CompoundU8(b), - v => { - return Err(NokhwaError::SetPropertyError { - property: id.to_string(), - value: v.to_string(), - error: "not supported".to_string(), - }) - } - }; - self.device - .set_control(Control { - id: known_camera_control_to_id(id), - value: conv_value, - }) - .map_err(|why| NokhwaError::SetPropertyError { - property: id.to_string(), - value: format!("{:?}", value), - error: why.to_string(), - })?; - // verify - - let control = self.camera_control(id)?; - if control.value() != value { - return Err(NokhwaError::SetPropertyError { - property: id.to_string(), - value: format!("{:?}", value), - error: "Rejected".to_string(), - }); - } - Ok(()) - } - - fn open_stream(&mut self) -> Result<(), NokhwaError> { - let stream = match MmapStream::new(&self.device, v4l::buffer::Type::VideoCapture) { - Ok(s) => s, - Err(why) => return Err(NokhwaError::OpenStreamError(why.to_string())), - }; - self.stream_handle = Some(stream); - Ok(()) - } - - fn is_stream_open(&self) -> bool { - self.stream_handle.is_some() - } - - fn frame(&mut self) -> Result { - let cam_fmt = self.camera_format; - let raw_frame = self.frame_raw()?; - Ok(Buffer::new( - cam_fmt.resolution(), - &raw_frame, - cam_fmt.format(), - )) - } - - fn frame_raw(&mut self) -> Result, NokhwaError> { - match &mut self.stream_handle { - Some(sh) => match sh.next() { - Ok((data, _)) => Ok(Cow::Borrowed(data)), - Err(why) => Err(NokhwaError::ReadFrameError(why.to_string())), - }, - None => Err(NokhwaError::ReadFrameError( - "Stream Not Started".to_string(), - )), - } - } - - fn stop_stream(&mut self) -> Result<(), NokhwaError> { - if self.stream_handle.is_some() { - self.stream_handle = None; - } - Ok(()) - } -} - -fn fourcc_to_frameformat(fourcc: FourCC) -> Option { - match fourcc.str().ok()? { - "YUYV" => Some(FrameFormat::YUYV), - "MJPG" => Some(FrameFormat::MJPEG), - "GRAY" => Some(FrameFormat::GRAY), - "RGB3" => Some(FrameFormat::RAWRGB), - "NV12" => Some(FrameFormat::NV12), - _ => None, - } -} - -fn frameformat_to_fourcc(fourcc: FrameFormat) -> FourCC { - match fourcc { - FrameFormat::MJPEG => FourCC::new(b"MJPG"), - FrameFormat::YUYV => FourCC::new(b"YUYV"), - FrameFormat::GRAY => FourCC::new(b"GRAY"), - FrameFormat::RAWRGB => FourCC::new(b"RGB3"), - FrameFormat::NV12 => FourCC::new(b"NV12"), - } -} diff --git a/src/lib.rs b/src/lib.rs index 80df5bb..4bc14a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,10 @@ //! //! The [`Camera`] struct is what you will likely use. //! -//! Please read the README for more. +//! The recommended default feature to enable is `input-native`. The library will not work without +//! at least one `input-*` feature enabled. +//! +//! Please read the README.md for more. /// Raw access to each of Nokhwa's backends. pub mod backends;