v4l2 facade crate for input-native

This commit is contained in:
l1npengtul
2022-11-18 02:45:28 +09:00
parent 242d723f65
commit faf8be2d43
5 changed files with 16 additions and 792 deletions
+10 -5
View File
@@ -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
+1 -1
View File
@@ -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"]
+1 -4
View File
@@ -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")
-781
View File
@@ -1,781 +0,0 @@
/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / 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<MmapStream<'a>>,
}
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<Self, NokhwaError> {
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::<Vec<CameraFormat>>();
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<Self, NokhwaError> {
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<Vec<Resolution>, 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<HashMap<Resolution, Vec<u32>>, 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<Vec<FrameFormat>, 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<CameraControl, NokhwaError> {
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<Vec<CameraControl>, 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::<Option<Vec<KnownCameraControlFlag>>>()
.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::<Result<Vec<CameraControl>, 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<Buffer, NokhwaError> {
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<Cow<[u8]>, 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<FrameFormat> {
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"),
}
}
+4 -1
View File
@@ -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;