in progress v4l2 implementation, mostly stable core

This commit is contained in:
l1npengtul
2025-01-16 19:17:58 +09:00
parent f0ffb0edcf
commit 33f078752b
26 changed files with 1286 additions and 1328 deletions
Generated
+309 -375
View File
File diff suppressed because it is too large Load Diff
+3 -12
View File
@@ -41,17 +41,12 @@ docs-features = []
test-fail-warning = []
[dependencies]
thiserror = "1.0"
paste = "1.0"
[dependencies.mozjpeg]
version = "0.10"
optional = true
[dependencies.dcv-color-primitives]
version = "0.6"
optional = true
[dependencies.nokhwa-core]
version = "0.2"
path = "nokhwa-core"
@@ -73,11 +68,11 @@ version = "0.2"
optional = true
[dependencies.wgpu]
version = "0.20"
version = "23"
optional = true
[dependencies.opencv]
version = "0.92"
version = "0.93"
default-features = false
features = ["videoio"]
optional = true
@@ -97,14 +92,10 @@ path = "nokhwa-bindings-macos"
optional = true
[dependencies.nokhwa-bindings-linux]
version = "0.1"
version = "0.2"
path = "nokhwa-bindings-linux"
optional = true
[dependencies.regex]
version = "1.7"
optional = true
[dependencies.web-sys]
version = "0.3"
features = [
Generated
+9 -9
View File
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1733097829,
"narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=",
"lastModified": 1736420959,
"narHash": "sha256-dMGNa5UwdtowEqQac+Dr0d2tFO/60ckVgdhZU9q2E2o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a",
"rev": "32af3611f6f05655ca166a0b1f47b57c762b5192",
"type": "github"
},
"original": {
@@ -36,11 +36,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1728538411,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
"lastModified": 1736320768,
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
"type": "github"
},
"original": {
@@ -62,11 +62,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1733193245,
"narHash": "sha256-nwvKoPi3S6XyliqBRuC+01QFF0k94ZOvnoZtbGi/ObM=",
"lastModified": 1736649126,
"narHash": "sha256-XCw5sv/ePsroqiF3lJM6Y2X9EhPdHeE47gr3Q8b0UQw=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "3458f7f946ba61d1a1069aedcc17d7b7616f23cd",
"rev": "162ab0edc2936508470199b2e8e6c444a2535019",
"type": "github"
},
"original": {
+48 -39
View File
@@ -5,50 +5,59 @@
rust-overlay.url = "github:oxalica/rust-overlay";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
outputs = {
self,
nixpkgs,
rust-overlay,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {
inherit system overlays;
inherit system;
overlays = [rust-overlay.overlays.default];
};
in
{
rustbin = pkgs.rust-bin.selectLatestNightlyWith (toolchain:
toolchain.default.override {
extensions = ["rust-src"];
});
in {
formatter = pkgs.alejandra;
devShells.default = pkgs.mkShell {
#LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
#BINDGEN_EXTRA_CLANG_ARGS = "-isystem ${pkgs.libclang.lib}/lib/clang/${flake-utils.lib.getVersion pkgs.clang}/include";
packages = [
rustbin
] ++ (with pkgs; [
llvmPackages.libclang.lib
llvmPackages.clang
pkg-config
cmake
vcpkg
rustPlatform.bindgenHook
xmlstarlet
opencv
alsa-lib
systemdLibs
cmake
fontconfig
linuxHeaders
v4l-utils
libv4l
pipewire
]);
buildInputs = with pkgs; [
rust-bin.stable.latest.default
rust-bin.stable.latest.rustfmt
rust-bin.stable.latest.clippy
];
nativeBuildInputs = [
pkgs.pkg-config
pkgs.cmake
pkgs.vcpkg
];
packages = with pkgs; [
rust-analyzer
pkg-config
opencv
alsa-lib
systemdLibs
cmake
fontconfig
linuxHeaders
rustPlatform.bindgenHook
llvmPackages.libclang.lib
llvmPackages.clang
v4l-utils
libv4l
];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
shellHook = ''
export LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
cargo version
env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library";
env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
shellHook = let
pathToRustProject = "/project/component[@name='RustProjectSettings']";
in
''
echo "WONDERHOOOOOY!!!!"
xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='explicitPathToStdlib']/@value" --value "${rustbin}/lib/rustlib/src/rust/library" .idea/workspace.xml
xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='toolchainHomeDirectory']/@value" --value "${rustbin}/bin" .idea/workspace.xml
'';
};
}
);
+10 -7
View File
@@ -1,22 +1,25 @@
[package]
name = "nokhwa-bindings-linux"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/l1npengtul/nokhwa"
description = "The V4L2 bindings crate for `nokhwa`"
keywords = ["v4l", "v4l2", "linux", "capture", "webcam"]
description = "The Linux V4L2 bindings crate for `nokhwa`"
keywords = ["v4l", "v4l2", "linux", "nokhwa", "webcam"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
v4l2 = ["v4l", "v4l2-sys-mit"]
pw = ["pipewire"]
[dependencies]
v4l = { version = "0.14", features = ["v4l2"], optional = true }
v4l2-sys-mit = { version = "0.3", optional = true }
[dependencies.pipewire]
version = "0.8"
optional = true
[dependencies.nokhwa-core]
version = "0.2"
path = "../nokhwa-core"
[target.'cfg(target_os="linux")'.dependencies]
v4l = { version = "0.14", optional = true }
v4l2-sys-mit = { version = "0.3", optional = true }
+394 -110
View File
@@ -1,44 +1,70 @@
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use v4l::{Device, Format, FourCC, Fraction};
use v4l2_sys_mit::{V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_BACKLIGHT_COMPENSATION, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, V4L2_CID_DO_WHITE_BALANCE, V4L2_CID_EXPOSURE, V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_GAIN, V4L2_CID_GAMMA, V4L2_CID_HUE, V4L2_CID_HUE_AUTO, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_PAN_ABSOLUTE, V4L2_CID_PAN_RELATIVE, V4L2_CID_SATURATION, V4L2_CID_SHARPNESS, V4L2_CID_TILT_ABSOLUTE, V4L2_CID_TILT_RELATIVE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE};
use v4l::device::Handle;
use std::collections::{HashMap, HashSet};
use std::mem;
use std::num::NonZeroI32;
use v4l::context::enum_devices;
use v4l::{Capabilities, Device, Format, FourCC, Fraction, FrameInterval};
use v4l::control::{Description, Flags, MenuItem, Type, Value};
use v4l::frameinterval::FrameIntervalEnum;
use v4l::prelude::MmapStream;
use v4l::video::{Capture as V4lCapture, Output};
use v4l::video::Output;
use v4l::video::output::Parameters;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::camera::{Camera, Open, Setting, Capture};
use nokhwa_core::properties::{CameraProperties, CameraPropertyFlag, CameraPropertyId, CameraPropertyValue};
use nokhwa_core::{define_back_and_fourth_control, define_back_and_fourth_frame_format};
use nokhwa_core::camera::{Camera, Capture, Setting};
use nokhwa_core::error::{NokhwaError, NokhwaResult};
use nokhwa_core::frame_format::FrameFormat;
use nokhwa_core::platform::{Backends, PlatformTrait};
use nokhwa_core::control::{ControlBody, ControlFlags, ControlId, ControlValue, ControlValueDescriptor, Controls};
use nokhwa_core::stream::Stream;
use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution};
use v4l2_sys_mit::{V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_CID_FOCUS_AUTO, V4L2_CID_AUTO_FOCUS_STATUS, V4L2_CID_ISO_SENSITIVITY, V4L2_CID_EXPOSURE_AUTO, V4L2_CID_AUTO_EXPOSURE_BIAS, V4L2_CID_EXPOSURE_METERING, V4L2_CID_EXPOSURE_ABSOLUTE, V4L2_CID_ISO_SENSITIVITY_AUTO, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_FLASH_LED_MODE, V4L2_CID_FLASH_STROBE, V4L2_CID_FLASH_STROBE_STATUS, V4L2_CID_CAMERA_ORIENTATION, V4L2_CTRL_FLAG_DISABLED, V4L2_CID_FLASH_STROBE_STOP, v4l2_querymenu};
use v4l::device::Handle;
use v4l::v4l2::ioctl;
use nokhwa_core::ranges::Range;
const NULL_FCC: &'static [u8; 4] = &[0x00, 0x00, 0x00, 0x00];
fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> CameraInformation {
let name = capabilities.card;
let description = capabilities.driver;
let misc = format!("{} v{}.{}.{} Flags: {}", capabilities.bus, capabilities.version.0, capabilities.version.1, capabilities.version.2, capabilities.capabilities);
pub use v4l2_sys_mit::*;
pub use v4l::*;
fn func_u8_8_to_fcc(u8_8: [u8; 8]) -> FrameFormatIntermediate {
FrameFormatIntermediate([u8_8[0], u8_8[1], u8_8[2], u8_8[3]])
CameraInformation::new(name, description, misc, CameraIndex::Index(index))
}
fn func_fcc_to_u8_8(u8_4: [u8; 4]) -> [u8; 8] {
[u8_4[0], u8_4[1], u8_4[2], u8_4[3], 0x00, 0x00, 0x00, 0x00]
macro_rules! define_back_and_forth {
( $($frame_format:expr => $fourcc:expr ,)+ ) => {
fn frame_format_to_fourcc(frame_format: FrameFormat) -> Result<FourCC, NokhwaError> {
match frame_format {
$(
$frame_format => Ok(FourCC::new($fourcc)),
)+
FrameFormat::Custom(def) => {
// if 4-7 is set (non-null) return an error.
if def[4..=7] != [0x00, 0x00, 0x00, 0x00] {
return Err(NokhwaError::ConversionError("Invalid: Custom bytes 4-7 are set (linux only uses 0-3)".to_string()))
}
Ok(FourCC::new(&[def[0], def[1], def[2], def[3]]))
}
_ => {
return Err(NokhwaError::ConversionError("Unsupported FrameFormat".to_string()))
}}
}
fn fourcc_to_frame_format(four_cc: FourCC) -> FrameFormat {
match &four_cc.repr {
$(
$fourcc => $frame_format
)+
custom => FrameFormat::Custom([ custom[0], custom[1], custom[2], custom[3], 0x00, 0x00, 0x00, 0x00 ])
}
}
}
}
fn value_to_fcc_type(v: &[u8; 4]) -> [u8;4] {
*v
}
define_back_and_fourth_frame_format!([u8;4], {
define_back_and_forth!(
FrameFormat::H265 => b"HEVC",
FrameFormat::H264 => b"H264",
FrameFormat::Avc1 => b"AVC1",
FrameFormat::H263 => b"H263",
FrameFormat::Av1 => b"AV1F",
FrameFormat::Avc1 => b"AVC1",
FrameFormat::Mpeg1 => b"MPG1",
FrameFormat::Mpeg2 => b"MPG2",
FrameFormat::Mpeg4 => b"MPG4",
@@ -50,107 +76,365 @@ define_back_and_fourth_frame_format!([u8;4], {
FrameFormat::Yuyv422 => b"YUYV",
FrameFormat::Uyvy422 => b"UYVY",
FrameFormat::Yvyu422 => b"YVYU",
FrameFormat::Yv12 => b"YV12",
FrameFormat::Nv12 => b"NV12",
FrameFormat::Nv21 => b"NV21",
FrameFormat::Yv12 => b"YV12",
FrameFormat::I420 => b"YU12",
FrameFormat::Yvu9 => b"YVU9",
FrameFormat::Luma8 => b"GREY",
FrameFormat::Luma16 => b"Y16 ",
FrameFormat::Depth16 => b"Z16 ",
FrameFormat::Rgb332 => b"RGB3",
FrameFormat::Rgb332 => b"RGB1",
FrameFormat::Rgb888 => b"RGB3",
FrameFormat::Bgr888 => b"BGR3",
FrameFormat::BgrA8888 => b"RA24",
FrameFormat::RgbA8888 => b"AB24",
FrameFormat::ARgb8888 => b"BA24",
FrameFormat::Rgb555 => b"RX15",
FrameFormat::Rgb565 => b"RGBP",
FrameFormat::Bayer8 => b"BA81",
FrameFormat::Bayer16 => b"BYR2",
}, func_u8_8_to_fcc, func_fcc_to_u8_8, value_to_fcc_type);
);
fn linux_id_to_str(id: u32) -> String {
id.to_string()
}
fn str_to_linux_id(id: &str) -> Option<u32> {
u32::from_str(id).ok()
}
define_back_and_fourth_control!(u32, {
CameraPropertyId::BacklightCompensation, None => V4L2_CID_BACKLIGHT_COMPENSATION,
CameraPropertyId::Brightness, None => V4L2_CID_BRIGHTNESS,
CameraPropertyId::Contrast, None => V4L2_CID_CONTRAST,
CameraPropertyId::Exposure, None => V4L2_CID_EXPOSURE,
CameraPropertyId::Focus, Some(CameraPropertyFlag::Relative) => V4L2_CID_FOCUS_RELATIVE,
CameraPropertyId::Focus, Some(CameraPropertyFlag::Absolute) => V4L2_CID_FOCUS_ABSOLUTE,
CameraPropertyId::Gamma, None => V4L2_CID_GAMMA,
CameraPropertyId::Gain, None => V4L2_CID_GAIN,
CameraPropertyId::Hue, None => V4L2_CID_HUE,
CameraPropertyId::Hue, Some(CameraPropertyFlag::Automatic) => V4L2_CID_HUE_AUTO,
CameraPropertyId::Iris, Some(CameraPropertyFlag::Relative) => V4L2_CID_IRIS_RELATIVE,
CameraPropertyId::Iris, Some(CameraPropertyFlag::Absolute) => V4L2_CID_IRIS_ABSOLUTE,
CameraPropertyId::Saturation, None => V4L2_CID_SATURATION,
CameraPropertyId::Sharpness, None => V4L2_CID_SHARPNESS,
CameraPropertyId::Pan, Some(CameraPropertyFlag::Absolute) => V4L2_CID_PAN_ABSOLUTE,
CameraPropertyId::Pan, Some(CameraPropertyFlag::Relative) => V4L2_CID_PAN_RELATIVE,
// CameraPropertyId::Pan, None => V4L2_CID_PAN_ABSOLUTE,
// CameraPropertyId::Tilt, None => V4L2_CID_TILT_ABSOLUTE,
CameraPropertyId::Tilt, Some(CameraPropertyFlag::Absolute) => V4L2_CID_TILT_ABSOLUTE,
CameraPropertyId::Tilt, Some(CameraPropertyFlag::Relative) => V4L2_CID_TILT_RELATIVE,
// CameraPropertyId::Zoom, None => V4L2_CID_ZOOM_ABSOLUTE,
CameraPropertyId::WhiteBalance, None => V4L2_CID_WHITE_BALANCE_TEMPERATURE,
CameraPropertyId::WhiteBalance, Some(CameraPropertyFlag::Automatic) => V4L2_CID_AUTO_WHITE_BALANCE,
CameraPropertyId::WhiteBalance, Some(CameraPropertyFlag::Enable) => V4L2_CID_DO_WHITE_BALANCE,
CameraPropertyId::Zoom, Some(CameraPropertyFlag::Absolute) => V4L2_CID_ZOOM_ABSOLUTE,
CameraPropertyId::Zoom, Some(CameraPropertyFlag::Relative) => V4L2_CID_ZOOM_RELATIVE,
CameraPropertyId::Zoom, Some(CameraPropertyFlag::Continuous) => V4L2_CID_ZOOM_CONTINUOUS,
// CameraPropertyId::Iris, None => V4L2_CID_IRIS_ABSOLUTE,
}, linux_id_to_str, str_to_linux_id);
pub struct DeviceInner {
device: Device,
}
impl DeviceInner {
pub fn new(index: usize) -> Result<Self, NokhwaError> {
let device = Device::new(index).map_err(|why| NokhwaError::OpenDeviceError(index.to_string(), why.to_string()))?;
Ok(DeviceInner { device })
}
pub fn resolutions(&self, fourcc: FourCC) -> Result<Vec<Resolution>, NokhwaError> {
let resolutions = self.device.enum_framesizes(fourcc.into()).map_err(|why| NokhwaError::GetPropertyError { property: "enum_framesizes".to_string(), error: why.to_string() })?.into_iter().map(|r| r.size.to_discrete().into_iter()).flatten().map(|res| Resolution::new(res.width, res.height) ).collect::<Vec<Resolution>>();
Ok(resolutions)
}
pub fn frame_rates(&self, fourcc: FourCC, resolution: Resolution) -> Result<Vec<FrameRate>, NokhwaError> {
let frame_rates = match self.device.enum_frameintervals(fourcc, resolution.width(), resolution.height()) {
Ok(intervals) => intervals.into_iter().map(|x| match x.interval {
FrameIntervalEnum::Discrete(d) => vec![d],
FrameIntervalEnum::Stepwise(step) => {
let mut temp_vec = vec![];
for rate in (step.min.numerator..step.max.numerator).step_by(step.step.numerator as usize) {
temp_vec.push(Fraction::new(rate, step.min.denominator))
}
temp_vec
macro_rules! define_control_id_conv {
( $($control_id:expr => $v4l_cid:expr ,)+ ) => {
fn control_id_to_cid(control_id: ControlId) -> Result<u32, NokhwaError> {
match control_id {
$(
$control_id => Ok($v4l_cid)
)+
ControlId::PlatformSpecific(specific_id) => {
u32::try_from(specific_id).map_err(|why| {
NokhwaError::ConversionError("ID must be a u32".to_string())
})
}
}),
Err(why) => {
return Err(NokhwaError::GetPropertyError { property: "enum_frameintervals".to_string(), error: why.to_string() })
_ => Err(NokhwaError::ConversionError("Could not match ID".to_string())
)
}
}.into_iter().flatten().map(|x| FrameRate::new(x.numerator, x.denominator)).collect::<Vec<FrameRate>>();
Ok(frame_rates)
}
}
pub fn properties(&self) -> CameraProperties {
}
pub fn inner(&self) -> &Device {
&self.device
fn cid_to_control_id(cid: u32) -> ControlId {
match cid {
$(
$v4l_cid => $control_id
)+
other_id => ControlId::PlatformSpecific(other_id as u64)
}
}
}
}
pub struct StreamInner<'a> {
stream: MmapStream<'a>
define_control_id_conv!(
ControlId::FocusMode => V4L2_CID_FOCUS_AUTO,
ControlId::FocusAutoRange => V4L2_CID_AUTO_FOCUS_RANGE,
ControlId::FocusAbsolute => V4L2_CID_FOCUS_ABSOLUTE,
ControlId::FocusRelative => V4L2_CID_FOCUS_RELATIVE,
ControlId::FocusStatus => V4L2_CID_AUTO_FOCUS_STATUS,
ControlId::ExposureMode => V4L2_CID_EXPOSURE_AUTO,
ControlId::ExposureBias => V4L2_CID_AUTO_EXPOSURE_BIAS,
ControlId::ExposureMetering => V4L2_CID_EXPOSURE_METERING,
ControlId::ExposureAbsolute =>V4L2_CID_EXPOSURE_ABSOLUTE,
ControlId::IsoMode =>V4L2_CID_ISO_SENSITIVITY_AUTO,
ControlId::IsoSensitivity => V4L2_CID_ISO_SENSITIVITY,
ControlId::ApertureAbsolute => V4L2_CID_IRIS_ABSOLUTE,
ControlId::ApertureRelative => V4L2_CID_IRIS_RELATIVE,
ControlId::WhiteBalanceMode => V4L2_CID_AUTO_WHITE_BALANCE,
ControlId::WhiteBalanceTemperature => V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
ControlId::ZoomContinuous => V4L2_CID_ZOOM_CONTINUOUS,
ControlId::ZoomRelative => V4L2_CID_ZOOM_RELATIVE,
ControlId::ZoomAbsolute => V4L2_CID_ZOOM_ABSOLUTE,
ControlId::LightingMode => V4L2_CID_FLASH_LED_MODE,
ControlId::LightingStart => V4L2_CID_FLASH_STROBE,
ControlId::LightingStop => V4L2_CID_FLASH_STROBE_STOP,
ControlId::LightingStatus => V4L2_CID_FLASH_STROBE_STATUS,
ControlId::Orientation => V4L2_CID_CAMERA_ORIENTATION,
);
fn flags(flags: Flags) -> HashSet<ControlFlags> {
let mut output_flags = HashSet::new();
if flags.intersects(Flags::DISABLED) {
output_flags.insert(ControlFlags::Disabled);
}
if flags.intersects(Flags::GRABBED) {
output_flags.insert(ControlFlags::Busy);
}
if flags.intersects(Flags::READ_ONLY) {
output_flags.insert(ControlFlags::ReadOnly);
}
if flags.intersects(Flags::UPDATE) {
output_flags.insert(ControlFlags::CascadingUpdates);
}
if flags.intersects(Flags::SLIDER) {
output_flags.insert(ControlFlags::Slider);
}
if flags.intersects(Flags::WRITE_ONLY) {
output_flags.insert(ControlFlags::WriteOnly);
}
if flags.intersects(Flags::VOLATILE) {
output_flags.insert(ControlFlags::Volatile);
}
if flags.intersects(Flags::EXECUTE_ON_WRITE) {
output_flags.insert(ControlFlags::ExecuteOnWrite);
}
output_flags
}
fn convert_description_to_ctrl_body(description: Description) -> Option<ControlBody> {
let flags = flags(description.flags);
let (descriptor, default) = match description.typ {
Type::Integer | Type::Integer64 => {
(
ControlValueDescriptor::Integer(Range::new(description.minimum, description.maximum, Some(description.step as i64))),
Some(ControlValue::Integer(description.default))
)
}
Type::U8 => {
(
ControlValueDescriptor::Integer(Range::new(0, u8::MAX_VALUE as i64, Some(description.step as i64))),
Some(ControlValue::Integer(description.default))
)
}
Type::U16 => {
(
ControlValueDescriptor::Integer(Range::new(0, u16::MAX_VALUE as i64, Some(description.step as i64))),
Some(ControlValue::Integer(description.default))
)
}
Type::U32 => {
(
ControlValueDescriptor::Integer(Range::new(0, u32::MAX_VALUE as i64, Some(description.step as i64))),
Some(ControlValue::Integer(description.default))
)
}
Type::String => {
(
ControlValueDescriptor::String,
None,
)
}
Type::Boolean => {
(
ControlValueDescriptor::Boolean,
Some(ControlValue::Boolean(description.default != 0))
)
}
Type::Bitmask => {
(
ControlValueDescriptor::BitMask,
Some(ControlValue::BitMask(description.default))
)
}
Type::IntegerMenu | Type::Menu => {
// We just initialize the values to Null for now.
// We fill it out later.
// our keys
let descriptor = match description.items {
Some(items) => {
ControlValueDescriptor::Menu(items.into_iter().map(|(idx, menu_item)| {
(ControlValue::Integer(idx as i64), match menu_item {
MenuItem::Name(name) => ControlValue::String(name),
MenuItem::Value(v) => ControlValue::Integer(*v),
})
}).collect::<HashMap<ControlValue, ControlValue>>())
}
// This can probably never happen so we just immediately return if this bad thing
// happens somehow
None => return None,
};
(
descriptor,
Some(ControlValue::Integer(description.default))
)
}
Type::Button => {
(
ControlValueDescriptor::Null,
None,
)
}
// we simply will not support control class.
// if someone needs it we can fix it later.
// honestly the whole concept scares me.
// i also have no idea on what an Area could be
// v4l2 docs are very sparse with this info. https://docs.kernel.org/userspace-api/media/v4l/ext-ctrls-image-source.html#c.v4l2_area
_ => return None,
};
Some(
ControlBody::new(
flags,
descriptor,
default
)
)
}
pub struct V4L2Platform {}
impl PlatformTrait for V4L2Platform {
const PLATFORM: Backends = Backends::Video4Linux2;
type Camera = V4L2Camera;
fn block_on_permission(&mut self) -> NokhwaResult<()> {
Ok(())
}
fn check_permission_given(&mut self) -> bool {
true
}
fn query(&mut self) -> NokhwaResult<Vec<CameraInformation>> {
Ok(enum_devices().into_iter()
.map(|v4l_node| {
let index = v4l_node.index();
// open camera for capabilities. if we dont get any, dont return the camera
Device::new(index).map(|dev|
dev.query_caps().map(|caps| {
index_capabilities_to_camera_info(index as u32, caps)
}).ok()
).ok().flatten()
}).flatten().collect::<Vec<_>>())
}
fn open(&mut self, index: &CameraIndex) -> NokhwaResult<Self::Camera> {
todo!()
}
}
pub struct V4L2Camera {
device: Device,
camera_format: Option<CameraFormat>,
camera_index: CameraIndex,
controls: Option<Controls>,
}
impl Setting for V4L2Camera {
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError> {
let mut formats = vec![];
for frame_format in self.device.enum_formats().map_err(|why| {
NokhwaError::GetPropertyError { property: "enum_formats".to_string(), error: why.to_string() }
})?.into_iter().map(|desc| {
fourcc_to_frame_format(desc.fourcc)
}) {
formats.extend(
self.enumerate_resolution_and_frame_rates(frame_format)?.into_iter().flat_map(|(resolution, frame_rates)| {
frame_rates.into_iter().map(|frame_rate| {
CameraFormat::new(resolution, frame_format, frame_rate)
})
})
);
}
Ok(formats)
}
fn enumerate_resolution_and_frame_rates(&self, frame_format: FrameFormat) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError> {
let fourcc = frame_format_to_fourcc(frame_format)?;
let resolutions = self.device.enum_framesizes(fourcc).map_err(|why| {
NokhwaError::GetPropertyError { property: "enum_framesizes".to_string(), error: why.to_string() }
})?.into_iter()
.flat_map(|frame_size| {
frame_size.size.to_discrete()
}).map(|discrete| { Resolution::new(discrete.width, discrete.height)
}).collect::<Vec<Resolution>>();
let v4l2_frame_intervals = resolutions.iter()
.map(|resolution| (*resolution, self.device.enum_frameintervals(fourcc, resolution.width(), resolution.height())))
.collect::<Result<Vec<(Resolution, Vec<FrameInterval>)>, std::io::Error>>()
.map_err(|why| {
NokhwaError::GetPropertyError { property: "enum_frameintervals".to_string(), error: why.to_string() }
})?;
Ok(v4l2_frame_intervals.into_iter().flatten().flat_map(|(resolution, interval)| {
match interval.interval {
FrameIntervalEnum::Discrete(discrete) => {
NonZeroI32::new(discrete.denominator as i32).map(|denominator| {
(resolution, vec![FrameRate::new(discrete.numerator as i32, denominator)])
})
}
FrameIntervalEnum::Stepwise(stepwise) => {
// we have to do this ourselves
// no logic to handle different or zero demoninator
if (stepwise.step.denominator != stepwise.max.denominator) || (stepwise.step.denominator != stepwise.min.denominator) {
return None
}
let min = stepwise.min.numerator as i32;
let max = stepwise.max.numerator as i32;
let step = stepwise.step.numerator as i32;
let denominator = stepwise.step.denominator as i32;
NonZeroI32::new(denominator).map(|denominator| {
(resolution, (min..max).step_by(step as usize).map(|numerator| {
FrameRate::new(numerator, denominator)
}).collect::<Vec<FrameRate>>())
})
}
}
}).flatten().collect::<HashMap<Resolution, Vec<FrameRate>>>())
}
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError> {
let fourcc = frame_format_to_fourcc(*camera_format.format())?;
self.device.set_format(
&Format::new(camera_format.width(), camera_format.height(), fourcc)
).map_err(|why| NokhwaError::SetPropertyError {
property: "set_format".to_string(),
value: format!("format: {camera_format} fourcc: {fourcc}"),
error: why.to_string(),
})?;
self.device.set_params(&Parameters::new(Fraction::new(*camera_format.frame_rate().numerator() as u32, *camera_format.frame_rate().denominator() as u32))).map_err(|why| {
NokhwaError::SetPropertyError {
property: "set_params".to_string(),
value: format!("{}", camera_format.frame_rate()),
error: why.to_string(),
}
})?;
Ok(())
}
fn controls(&self) -> &Controls {
match self.controls {
}
let properties = self.device.query_controls().map_err(|why| {
NokhwaError::GetPropertyError { property: "query_controls".to_string(), error: why.to_string() }
})?.into_iter().map(|description| {
let id = cid_to_control_id(description.id);
convert_description_to_ctrl_body(description).map(|body| {
(id, body)
})
}).flatten().collect::<HashMap<ControlId, ControlBody>>();
}
fn set_control(&mut self, property: &ControlId, value: ControlValue) -> Result<(), NokhwaError> {
todo!()
}
}
impl Capture for V4L2Camera {
fn open_stream(&mut self) -> Result<Stream, NokhwaError> {
todo!()
}
fn close_stream(&mut self) -> Result<(), NokhwaError> {
todo!()
}
}
impl Camera for V4L2Camera {}
+1 -1
View File
@@ -246,7 +246,7 @@ mod internal {
ffi::{c_float, c_void, CStr},
sync::Arc,
};
use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
const UTF8_ENCODING: usize = 4;
type CGFloat = c_float;
+2 -2
View File
@@ -46,7 +46,7 @@ pub mod wmf {
Arc,
},
};
use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
use windows::Win32::Media::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual};
use windows::Win32::Media::MediaFoundation::{
IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM,
@@ -1229,7 +1229,7 @@ pub mod wmf {
CameraFormat, CameraIndex, CameraInformation,
};
use std::borrow::Cow;
use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
pub fn initialize_mf() -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
+2 -3
View File
@@ -25,6 +25,8 @@ thiserror = "2.0"
bytes = "1.3"
flume = "0.11"
num-traits = "0.2"
derive_builder = "0.20"
ordered-float = "4.6"
[dependencies.num-rational]
version = "0.4"
@@ -57,8 +59,5 @@ optional = true
version = "0.3"
optional = true
[dependencies.rgb]
version = "0.8"
[package.metadata.docs.rs]
features = ["docs-features"]
+6 -4
View File
@@ -1,6 +1,6 @@
use crate::error::{NokhwaError};
use crate::frame_format::FrameFormat;
use crate::properties::{ControlId, ControlValue, Properties};
use crate::control::{ControlId, ControlValue, Controls};
use crate::types::{CameraFormat, FrameRate, Resolution};
use std::collections::HashMap;
use crate::stream::Stream;
@@ -15,9 +15,11 @@ pub trait Setting {
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
fn properties(&self) -> &Properties;
fn controls(&self) -> &Controls;
fn set_property(
fn refresh_controls(&mut self) -> Result<(), NokhwaError>;
fn set_control(
&mut self,
property: &ControlId,
value: ControlValue,
@@ -36,7 +38,7 @@ pub trait AsyncSetting {
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn properties_async(&self) -> &Properties;
async fn properties_async(&self) -> &Controls;
async fn set_property_async(
&mut self,
+425
View File
@@ -0,0 +1,425 @@
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use ordered_float::OrderedFloat;
use crate::error::{NokhwaError, NokhwaResult};
use crate::ranges::{Range, ValidatableRange};
pub type PlatformSpecificControlId = u64;
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlId {
FocusMode,
FocusAutoType,
FocusAutoRange,
FocusAbsolute,
FocusRelative,
FocusStatus,
ExposureMode,
ExposureBias,
ExposureMetering,
ExposureAbsolute,
ExposureRelative,
IsoMode,
IsoSensitivity,
ApertureAbsolute,
ApertureRelative,
WhiteBalanceMode,
WhiteBalanceTemperature,
ZoomContinuous,
ZoomRelative,
ZoomAbsolute,
LightingMode,
LightingStart,
LightingStop,
LightingStatus,
Orientation,
PlatformSpecific(PlatformSpecificControlId)
}
impl Display for ControlId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Control ID: {self:?}")
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Controls {
controls: HashMap<ControlId, ControlBody>,
values: HashMap<ControlId, ControlId>,
}
impl Controls {
pub fn new(device_controls: HashMap<ControlId, ControlBody>) -> Self {
Self {
controls: device_controls,
}
}
pub fn empty() -> Self {
Self::default()
}
pub fn control_value(&self, control_id: &ControlId) -> Option<&ControlBody> {
self.controls.get(control_id)
}
pub fn set_control_value(&mut self, control_id: &ControlId, value: ControlValue) -> NokhwaResult<()> {
// see if it exists
if let Some(control) = self.controls.get_mut(control_id) {
// FIXME: Remove this clone one day!
control.set_value(value.clone())?;
}
Err(NokhwaError::SetPropertyError {
property: control_id.to_string(),
value: value.to_string(),
error: "Not Found/Not Supported".to_string(),
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ControlBody {
flags: HashSet<ControlFlags>,
descriptor: ControlValueDescriptor,
default_value: Option<ControlValue>,
}
impl ControlBody {
pub fn new(control_flags: HashSet<ControlFlags>, control_value_descriptor: ControlValueDescriptor, default_value: Option<ControlValue>) -> Self {
Self {
flags: control_flags,
descriptor: control_value_descriptor,
default_value,
}
}
pub fn flags(&self) -> &HashSet<ControlFlags> {
&self.flags
}
pub fn descriptor(&self) -> &ControlValueDescriptor {
&self.descriptor
}
pub fn default_value(&self) -> &Option<ControlValue> {
&self.default_value
}
pub fn add_flag(&mut self, flag: ControlFlags) {
self.flags.insert(flag);
}
pub fn remove_flag(&mut self, flag: ControlFlags) -> bool {
self.flags.remove(&flag)
}
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlFlags {
Disabled,
Busy,
ReadOnly,
CascadingUpdates,
Inactive,
Slider,
WriteOnly,
Volatile,
ContinuousChange,
ExecuteOnWrite,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ControlValueDescriptor {
Null,
Integer(Range<i64>),
BitMask,
Float(Range<f64>),
String,
Boolean,
// Array of any values of singular type
Array(ControlValueDescriptor),
// Menu(Enum) of valid choices
// The keys are valid choices,
// the values represent what the choice is (usually a string or int).
Menu(HashMap<ControlValue, ControlValue>),
// lmao u deal with it
// max/min length set by range. step is ALWAYS zero.
Binary(Range<u64>),
// An area (Resolution) like type
Area {
width_limits: Range<i64>,
height_limits: Range<i64>,
},
}
impl ControlValueDescriptor {
pub fn validate(&self, value: &ControlValue) -> bool {
match self {
ControlValueDescriptor::Null => {
if let &ControlValue::Null = value {
return false
}
}
ControlValueDescriptor::Integer(int_range) => {
if let ControlValue::Integer(i) = value {
return int_range.validate(i)
}
}
ControlValueDescriptor::BitMask => {
if let &ControlValue::BitMask(_) = value {
return true
}
}
ControlValueDescriptor::Float(float_range) => {
if let ControlValue::Float(i) = value {
return float_range.validate(i)
}
}
ControlValueDescriptor::String => {
if let &ControlValue::String(_) = value {
return true
}
}
ControlValueDescriptor::Boolean => {
if let &ControlValue::Boolean(_) = value {
return true
}
}
ControlValueDescriptor::Array(arr) => {
if let &ControlValue::Array(_) = value {
return arr.is_valid_value(value)
}
}
ControlValueDescriptor::Binary(size_limits) => {
if let ControlValue::Binary(bin) = value {
return size_limits.validate(bin.len() as u64)
}
}
ControlValueDescriptor::Menu(choices) => {
if let ControlValue::EnumPick(choice) = value {
return choices.contains_key(choice)
}
}
ControlValueDescriptor::Area { width_limits, height_limits } => {
if let ControlValue::Area { width, height } = &value {
return width_limits.validate(width) && height_limits.validate(height)
}
}
}
false
}
}
//
// #[derive(Clone, Debug, PartialEq)]
// pub enum ControlValuePrimitiveDescriptor {
// Null,
// Integer(Range<i64>),
// BitMask,
// Float(Range<f64>),
// String,
// Binary,
// Boolean,
// }
//
// impl ControlValuePrimitiveDescriptor {
// pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool {
// match self {
// ControlValuePrimitiveDescriptor::Null => {
// if let ControlValuePrimitive::Null = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Integer(i) => {
// if let ControlValuePrimitive::Integer(v) = other {
// return i.validate(v)
// }
// }
// ControlValuePrimitiveDescriptor::BitMask => {
// if let ControlValuePrimitive::BitMask(_) = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Float(f) => {
// if let ControlValuePrimitive::Float(v) = other {
// return f.validate(v)
// }
// }
// ControlValuePrimitiveDescriptor::String => {
// if let ControlValuePrimitive::String(_) = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Boolean => {
// if let ControlValuePrimitive::Boolean(_) = other {
// return true
// }
// }
// }
// false
// }
//
// pub fn is_valid_value(&self, other: &ControlValue) -> bool {
// match self {
// ControlValuePrimitiveDescriptor::Null => {
// if let ControlValue::Null = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Integer(i) => {
// if let ControlValue::Integer(v) = other {
// return i.validate(v)
// }
// }
// ControlValuePrimitiveDescriptor::BitMask => {
// if let ControlValue::BitMask(_) = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Float(f) => {
// if let ControlValue::Float(v) = other {
// return f.validate(v)
// }
// }
// ControlValuePrimitiveDescriptor::String => {
// if let ControlValue::String(_) = other {
// return true
// }
// }
// ControlValuePrimitiveDescriptor::Boolean => {
// if let ControlValue::Boolean(_) = other {
// return true
// }
// }
// }
// false
// }
// }
//
// #[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
// pub enum ControlValuePrimitive {
// Null,
// Integer(i64),
// BitMask(i64),
// Float(OrderedFloat<f64>),
// String(String),
// Boolean(bool),
// }
//
// impl From<ControlValuePrimitive> for ControlValue {
// fn from(value: ControlValuePrimitive) -> Self {
// match value {
// ControlValuePrimitive::Null => ControlValue::Null,
// ControlValuePrimitive::Integer(i) => ControlValue::Integer(i),
// ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b),
// ControlValuePrimitive::Float(f) => ControlValue::Float(f),
// ControlValuePrimitive::String(s) => ControlValue::String(s),
// ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b),
// }
// }
// }
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd)]
pub enum ControlValue {
Null,
Integer(i64),
BitMask(i64),
Float(OrderedFloat<f64>),
String(String),
Boolean(bool),
Array(Vec<ControlValue>),
Binary(Vec<u8>),
EnumPick(Box<ControlValue>),
Area {
width: i64,
height: i64,
}
}
impl ControlValue {
pub fn is_primitive(&self) -> bool {
match self {
ControlValue::Null |
ControlValue::Integer(_) |
ControlValue::BitMask(_) |
ControlValue::Float(_) |
ControlValue::String(_)|
ControlValue::Boolean(_) |
ControlValue::Binary(_) |
ControlValue::Area { .. } => true,
_ => false,
}
}
// pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool {
// match other {
// ControlValuePrimitive::Null => {
// if let ControlValue::Null = self {
// return true
// }
// }
// ControlValuePrimitive::Integer(_) => {if let ControlValue::Integer(_) = self {return true}}
// ControlValuePrimitive::BitMask(_) => {if let ControlValue::BitMask(_) = self {return true}}
// ControlValuePrimitive::Float(_) => {if let ControlValue::Float(_) = self {return true}}
// ControlValuePrimitive::String(_) => {if let ControlValue::String(_) = self {return true}}
// ControlValuePrimitive::Boolean(_) => {if let ControlValue::Boolean(_) = self {return true}}
// }
// false
// }
pub fn same_type(&self, other: &ControlValue) -> bool {
match self {
ControlValue::Null => {
if let ControlValue::Null = other {
return true;
}
}
ControlValue::Integer(_) => {if let ControlValue::Integer(_) = other {
return true;
}}
ControlValue::BitMask(_) => {if let ControlValue::BitMask(_) = other {
return true;
}}
ControlValue::Float(_) => {if let ControlValue::Float(_) = other {
return true;
}}
ControlValue::String(_) => {if let ControlValue::String(_) = other {
return true;
}}
ControlValue::Boolean(_) => {if let ControlValue::Boolean(_) = other {
return true;
}}
ControlValue::Array(_) => {if let ControlValue::Array(_) = other {
return true;
}}
ControlValue::EnumPick(_) => {if let ControlValue::EnumPick(_) = other {
return true;
}}
ControlValue::Binary(_) => {if let ControlValue::Binary(_) = other {
return true;
}}
ControlValue::Area { .. } => {if let ControlValue::Area { .. } = other {
return true;
}}
}
false
}
}
impl Display for ControlValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Control Value: {self:?}")
}
}
-2
View File
@@ -24,8 +24,6 @@ pub type NokhwaResult<T> = Result<T, NokhwaError>;
#[allow(clippy::module_name_repetitions)]
#[derive(Error, Debug, Clone)]
pub enum NokhwaError {
#[error("Unitialized Camera. Call `init()` first!")]
UnitializedError,
#[error("Could not initialize {backend}: {error}")]
InitializeError { backend: Backends, error: String },
#[error("Could not shutdown {backend}: {error}")]
+3 -3
View File
@@ -63,11 +63,11 @@ pub enum FrameFormat {
// RGB Formats
Rgb332,
Rgb555,
Rgb565,
Rgb888,
Bgr888,
BgrA8888,
RgbA8888,
ARgb8888,
+1 -1
View File
@@ -28,7 +28,7 @@ pub mod error;
pub mod format_request;
pub mod frame_buffer;
pub mod frame_format;
pub mod properties;
pub mod control;
pub mod ranges;
pub mod traits;
pub mod types;
+9
View File
@@ -31,6 +31,10 @@ pub trait PlatformTrait {
fn query(&mut self) -> NokhwaResult<Vec<CameraInformation>>;
fn open(&mut self, index: &CameraIndex) -> NokhwaResult<Self::Camera>;
fn open_dynamic(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>> {
self.open(index).map(|cam| Box::new(cam))
}
}
#[cfg(feature = "async")]
@@ -45,4 +49,9 @@ pub trait AsyncPlatformTrait {
async fn query_async(&mut self) -> NokhwaResult<Vec<CameraInformation>>;
async fn open_async (&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>> {
self.open_async(index).await.map(|cam| Box::new(cam))
}
}
-446
View File
@@ -1,446 +0,0 @@
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
use crate::error::{NokhwaError, NokhwaResult};
use crate::ranges::{Range, ValidatableRange};
pub type PlatformSpecificControlId = u64;
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlId {
FocusMode,
FocusAutoType,
FocusAutoRange,
FocusAbsolute,
FocusRelative,
FocusStatus,
ExposureMode,
ExposureBias,
ExposureTime,
ExposureAutoPriority,
ExposureIsoMode,
ExposureIsoSensitivity,
ExposureApertureAbsolute,
ExposureApertureRelative,
WhiteBalanceMode,
WhiteBalanceTemperature,
ZoomMode,
LightingMode,
PlatformSpecific(PlatformSpecificControlId)
}
impl Display for ControlId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Control ID: {self:?}")
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Properties {
controls: HashMap<ControlId, ControlBody>,
}
impl Properties {
pub fn new(device_controls: HashMap<ControlId, ControlBody>) -> Self {
Self {
controls: device_controls,
}
}
pub fn empty() -> Self {
Self::default()
}
pub fn control_value(&self, control_id: &ControlId) -> Option<&ControlBody> {
self.controls.get(control_id)
}
pub fn set_control_value(&mut self, control_id: &ControlId, value: ControlValue) -> NokhwaResult<()> {
// see if it exists
if let Some(control) = self.controls.get_mut(control_id) {
// FIXME: Remove this clone one day!
control.set_value(value.clone())?;
}
Err(NokhwaError::SetPropertyError {
property: control_id.to_string(),
value: value.to_string(),
error: "Not Found/Not Supported".to_string(),
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ControlBody {
control_type: ControlType,
flags: HashSet<ControlFlags>,
descriptor: ControlValueDescriptor,
value: Option<ControlValue>,
default_value: Option<ControlValue>,
}
impl ControlBody {
pub fn new(control_type: ControlType, control_flags: HashSet<ControlFlags>, control_value_descriptor: ControlValueDescriptor, value: Option<ControlValue>, default_value: Option<ControlValue>) -> Self {
Self {
control_type,
flags: control_flags,
descriptor: control_value_descriptor,
value,
default_value,
}
}
pub fn control_type(&self) -> &ControlType {
&self.control_type
}
pub fn flags(&self) -> &HashSet<ControlFlags> {
&self.flags
}
pub fn descriptor(&self) -> &ControlValueDescriptor {
&self.descriptor
}
pub fn value(&self) -> &Option<ControlValue> {
&self.value
}
pub fn default_value(&self) -> &Option<ControlValue> {
&self.default_value
}
pub fn add_flag(&mut self, flag: ControlFlags) {
self.flags.insert(flag);
}
pub fn remove_flag(&mut self, flag: ControlFlags) -> bool {
self.flags.remove(&flag)
}
pub fn set_value(&mut self, value: ControlValue) -> NokhwaResult<Option<ControlValue>> {
if self.descriptor.validate(&value) {
return Err(NokhwaError::SetPropertyError {
property: "Control Body".to_string(),
value: value.to_string(),
error: "Failed to validate control value".to_string(),
})
}
let old = core::mem::replace(&mut self.value, Some(value));
Ok(old)
}
pub fn clear_value(&mut self) -> Option<ControlValue> {
core::mem::replace(&mut self.value, None)
}
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlType {
Button,
Integer,
Menu,
IntegerMenu,
BinaryMenu,
Bitmask,
String,
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlFlags {
Disabled,
Busy,
ReadOnly,
CascadingUpdates,
Inactive,
Slider,
WriteOnly,
ContinuousChange,
ExecuteOnWrite,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ControlValueDescriptor {
Null,
Integer(Range<i64>),
BitMask,
Float(Range<f64>),
String,
Boolean,
// Array of any values of singular type
Array(ControlValuePrimitiveDescriptor),
// Multiple Choice from array
MultiChoice(Vec<ControlValuePrimitiveDescriptor>),
// Singular Choice from array
Enum(Vec<ControlValuePrimitiveDescriptor>),
// Hashmap
Map(HashMap<String, ControlValuePrimitiveDescriptor>),
// A menu, where you pick a key-value
Menu(HashMap<String, ControlValuePrimitiveDescriptor>)
}
impl ControlValueDescriptor {
pub fn validate(&self, value: &ControlValue) -> bool {
match self {
ControlValueDescriptor::Null => {
if let &ControlValue::Null = value {
return false
}
}
ControlValueDescriptor::Integer(int_range) => {
if let ControlValue::Integer(i) = value {
return int_range.validate(i)
}
}
ControlValueDescriptor::BitMask => {
if let &ControlValue::BitMask(_) = value {
return true
}
}
ControlValueDescriptor::Float(float_range) => {
if let ControlValue::Float(i) = value {
return float_range.validate(i)
}
}
ControlValueDescriptor::String => {
if let &ControlValue::String(_) = value {
return true
}
}
ControlValueDescriptor::Boolean => {
if let &ControlValue::Boolean(_) = value {
return true
}
}
ControlValueDescriptor::Array(arr) => {
if let &ControlValue::Array(_) = value {
return arr.is_valid_value(value)
}
}
ControlValueDescriptor::MultiChoice(choices) => {
if let ControlValue::Array(values) = value {
for v in values {
let mut contains = false;
let vl: ControlValue = v.clone().into();
for choice in choices {
if choice.is_valid_value(&vl) {
contains = true;
break;
}
}
return contains
}
}
}
ControlValueDescriptor::Enum(choices) => {
for choice in choices {
return choice.is_valid_value(&value)
}
}
ControlValueDescriptor::Map(map) => {
if let ControlValue::Map(setting_map) = &value {
for (setting_key, setting_value) in setting_map {
if let Some(descriptor) = map.get(setting_key) {
return !descriptor.is_valid_primitive_value(setting_value)
}
}
}
}
ControlValueDescriptor::Menu(menu) => {
if let ControlValue::KeyValue(k, v) = &value {
if let Some(descriptor) = menu.get(k) {
return descriptor.is_valid_primitive_value(v)
}
}
}
}
false
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ControlValuePrimitiveDescriptor {
Null,
Integer(Range<i64>),
BitMask,
Float(Range<f64>),
String,
Boolean,
}
impl ControlValuePrimitiveDescriptor {
pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool {
match self {
ControlValuePrimitiveDescriptor::Null => {
if let ControlValuePrimitive::Null = other {
return true
}
}
ControlValuePrimitiveDescriptor::Integer(i) => {
if let ControlValuePrimitive::Integer(v) = other {
return i.validate(v)
}
}
ControlValuePrimitiveDescriptor::BitMask => {
if let ControlValuePrimitive::BitMask(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Float(f) => {
if let ControlValuePrimitive::Float(v) = other {
return f.validate(v)
}
}
ControlValuePrimitiveDescriptor::String => {
if let ControlValuePrimitive::String(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Boolean => {
if let ControlValuePrimitive::Boolean(_) = other {
return true
}
}
}
false
}
pub fn is_valid_value(&self, other: &ControlValue) -> bool {
match self {
ControlValuePrimitiveDescriptor::Null => {
if let ControlValue::Null = other {
return true
}
}
ControlValuePrimitiveDescriptor::Integer(i) => {
if let ControlValue::Integer(v) = other {
return i.validate(v)
}
}
ControlValuePrimitiveDescriptor::BitMask => {
if let ControlValue::BitMask(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Float(f) => {
if let ControlValue::Float(v) = other {
return f.validate(v)
}
}
ControlValuePrimitiveDescriptor::String => {
if let ControlValue::String(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Boolean => {
if let ControlValue::Boolean(_) = other {
return true
}
}
}
false
}
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum ControlValuePrimitive {
Null,
Integer(i64),
BitMask(i64),
Float(f64),
String(String),
Boolean(bool),
}
impl From<ControlValuePrimitive> for ControlValue {
fn from(value: ControlValuePrimitive) -> Self {
match value {
ControlValuePrimitive::Null => ControlValue::Null,
ControlValuePrimitive::Integer(i) => ControlValue::Integer(i),
ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b),
ControlValuePrimitive::Float(f) => ControlValue::Float(f),
ControlValuePrimitive::String(s) => ControlValue::String(s),
ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ControlValue {
Null,
Integer(i64),
BitMask(i64),
Float(f64),
String(String),
Boolean(bool),
Array(Vec<ControlValuePrimitive>),
KeyValue(String, ControlValuePrimitive),
Map(HashMap<String, ControlValuePrimitive>),
}
impl ControlValue {
pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool {
match other {
ControlValuePrimitive::Null => {
if let ControlValue::Null = self {
return true
}
}
ControlValuePrimitive::Integer(_) => {if let ControlValue::Integer(_) = self {return true}}
ControlValuePrimitive::BitMask(_) => {if let ControlValue::BitMask(_) = self {return true}}
ControlValuePrimitive::Float(_) => {if let ControlValue::Float(_) = self {return true}}
ControlValuePrimitive::String(_) => {if let ControlValue::String(_) = self {return true}}
ControlValuePrimitive::Boolean(_) => {if let ControlValue::Boolean(_) = self {return true}}
}
false
}
pub fn same_type(&self, other: &ControlValue) -> bool {
match self {
ControlValue::Null => {
if let ControlValue::Null = other {
return true;
}
}
ControlValue::Integer(_) => {if let ControlValue::Integer(_) = other {
return true;
}}
ControlValue::BitMask(_) => {if let ControlValue::BitMask(_) = other {
return true;
}}
ControlValue::Float(_) => {if let ControlValue::Float(_) = other {
return true;
}}
ControlValue::String(_) => {if let ControlValue::String(_) = other {
return true;
}}
ControlValue::Boolean(_) => {if let ControlValue::Boolean(_) = other {
return true;
}}
ControlValue::Array(_) => {if let ControlValue::Array(_) = other {
return true;
}}
ControlValue::KeyValue(_, _) => {if let ControlValue::KeyValue(_, _) = other {
return true;
}}
ControlValue::Map(_) => {if let ControlValue::Map(_) = other {
return true;
}}
}
false
}
}
impl Display for ControlValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Control Value: {self:?}")
}
}
+42 -294
View File
@@ -1,13 +1,7 @@
use crate::error::NokhwaError;
use core::fmt::{Debug, Display, Formatter};
use std::collections::hash_map::Keys;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::{Div, Rem, Sub};
/// Failed to validate.
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct RangeValidationFailure;
use ordered_float::OrderedFloat;
/// A range type that can be validated.
pub trait ValidatableRange {
@@ -21,34 +15,32 @@ pub trait ValidatableRange {
/// Creates a range of values.
///
/// Inclusive by default.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Range<T> {
minimum: Option<T>,
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Range<T> where T: RangeItem
{
minimum: T,
lower_inclusive: bool,
maximum: Option<T>,
maximum: T,
upper_inclusive: bool,
preferred: T,
step: Option<T>,
}
impl<T> Range<T> where T: Copy {
/// Create an upper and lower inclusive [`Range`]
pub fn new(preferred: T, min: Option<T>, max: Option<T>, step: Option<T>) -> Self {
pub fn new(min: T, max: T, step: Option<T>) -> Self {
Self {
minimum: min,
lower_inclusive: true,
maximum: max,
upper_inclusive: true,
preferred,
step,
}
}
pub fn with_inclusive(
preferred: T,
min: Option<T>,
min: T,
lower_inclusive: bool,
max: Option<T>,
max: T,
upper_inclusive: bool,
step: Option<T>
) -> Self {
@@ -57,22 +49,10 @@ impl<T> Range<T> where T: Copy {
lower_inclusive,
maximum: max,
upper_inclusive,
preferred,
step,
}
}
pub fn exact(preferred: T) -> Self {
Self {
minimum: None,
lower_inclusive: true,
maximum: None,
upper_inclusive: true,
preferred,
step: None,
}
}
pub fn set_minimum(&mut self, minimum: Option<T>) {
self.minimum = minimum;
}
@@ -88,24 +68,18 @@ impl<T> Range<T> where T: Copy {
pub fn set_step(&mut self, step: T) {
self.step = Some(step);
}
pub fn set_preferred(&mut self, preferred: T) {
self.preferred = preferred;
}
pub fn minimum(&self) -> Option<T> {
pub fn minimum(&self) -> T {
self.minimum
}
pub fn lower_inclusive(&self) -> bool {
self.lower_inclusive
}
pub fn maximum(&self) -> Option<T> {
pub fn maximum(&self) -> T {
self.maximum
}
pub fn upper_inclusive(&self) -> bool {
self.upper_inclusive
}
pub fn preferred(&self) -> T {
self.preferred
}
pub fn step(&self) -> Option<T> {
self.step
}
@@ -113,20 +87,32 @@ impl<T> Range<T> where T: Copy {
impl<T> ValidatableRange for Range<T>
where
T: SimpleRangeItem,
T: RangeItem,
{
type Validation = T;
fn validate(&self, value: &T) -> bool {
num_range_validate(
self.minimum,
self.maximum,
self.preferred,
self.lower_inclusive,
self.upper_inclusive,
self.step,
*value,
)
let l_comparison_fn = match self.lower_inclusive {
true => T::ge,
false => T::gt,
};
let u_comparison_fn = match self.upper_inclusive {
true => T::le,
false => T::lt,
};
if !(l_comparison_fn(&self.minimum, value) && u_comparison_fn(&self.maximum, value)) {
return false
}
// check step
if let Some(step) = self.step {
let step_chk_value = *value - self.minimum;
return step_chk_value % step == 0;
}
return true
}
}
@@ -136,11 +122,10 @@ where
{
fn default() -> Self {
Range {
minimum: None,
minimum: T::default(),
lower_inclusive: true,
maximum: None,
maximum: T::default(),
upper_inclusive: true,
preferred: T::default(),
step: None,
}
}
@@ -153,198 +138,15 @@ where
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let lower_inclusive_char = bool_to_inclusive_char(self.lower_inclusive, false);
let upper_inclusive_char = bool_to_inclusive_char(self.upper_inclusive, true);
let default = &self.preferred;
write!(
f,
"Range: {lower_inclusive_char}{:?}, {:?}{upper_inclusive_char}, Preferred: {default:?}",
"Range: {lower_inclusive_char}{:?}, {:?}{upper_inclusive_char}",
self.minimum, self.maximum
)
}
}
#[derive(Clone, Debug)]
pub struct Options<T> {
default: Option<T>,
available: Vec<T>,
}
impl<T> Options<T>
where
T: Clone + Debug + PartialEq,
{
pub fn new(values: Vec<T>, default_value: Option<T>) -> Self {
Self {
default: default_value,
available: values,
}
}
pub fn default_value(&self) -> Option<&T> {
self.default.as_ref()
}
pub fn available(&self) -> &[T] {
&self.available
}
}
impl<T> ValidatableRange for Options<T>
where
T: Clone + PartialEq,
{
type Validation = T;
fn validate(&self, value: &Self::Validation) -> bool {
self.available.contains(value)
}
}
impl<T> Display for Options<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let default = default_to_string(&self.default);
write!(
f,
"Options: Available {:?}, Default: {default}",
self.available
)
}
}
#[derive(Clone, Debug)]
pub struct KeyValue<K, V>
where
K: Clone + Debug + Hash + Eq,
V: Clone + Debug,
{
defaults: HashMap<K, V>,
}
impl<K, V> KeyValue<K, V>
where
K: Clone + Debug + Hash + Eq,
V: Clone + Debug,
{
pub fn new(default: HashMap<K, V>) -> Self {
Self { defaults: default }
}
pub fn available_keys(&self) -> Keys<'_, K, V> {
self.defaults.keys()
}
pub fn by_key(&self, key: &K) -> Option<&V> {
self.defaults.get(key)
}
}
impl<K, V> Display for KeyValue<K, V>
where
K: Clone + Debug + Hash + Eq,
V: Clone + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// TODO: pretty print?
write!(f, "Key Value Pairs: {:?}", self.defaults)
}
}
#[derive(Clone, Debug)]
pub struct ArrayRange<T> {
appendable_options: Vec<T>,
default_options: Vec<T>,
}
impl<T> ArrayRange<T>
where
T: Clone + Debug + PartialEq,
{
pub fn new(appendable: Vec<T>, default: Vec<T>) -> Result<Self, NokhwaError> {
for option in &default {
if !appendable.contains(option) {
return Err(NokhwaError::StructureError { structure: "ArrayRange".to_string(), error: "Attempted to add an undependable option to default option - ILLEGAL! - If you got this while using a driver, this is a bug! Please report to https://github.com/l1npengtul/nokhwa/issues!".to_string() });
}
}
Ok(Self {
appendable_options: appendable,
default_options: default,
})
}
pub fn appendable_options(&self) -> &[T] {
&self.appendable_options
}
pub fn default_options(&self) -> &[T] {
&self.default_options
}
}
impl<T> ValidatableRange for ArrayRange<T>
where
T: PartialEq,
{
type Validation = T;
fn validate(&self, value: &Self::Validation) -> bool {
self.appendable_options.contains(value)
}
}
impl<T> Display for ArrayRange<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ArrayRange: Available Options: {:?}, Default: {:?}",
self.appendable_options, self.default_options
)
}
}
#[derive(Clone, Debug)]
pub struct Simple<T> {
default: Option<T>,
}
impl<T> Simple<T>
where
T: Clone + Debug,
{
pub fn new(default: Option<T>) -> Self {
Self { default }
}
pub fn default_value(&self) -> Option<&T> {
self.default.as_ref()
}
}
impl<T> ValidatableRange for Simple<T> {
type Validation = T;
fn validate(&self, _: &Self::Validation) -> bool {
true
}
}
impl<T> Display for Simple<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let default = default_to_string(&self.default);
write!(f, "Simple (Any Value): Default Value: {default}")
}
}
fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char {
match inclusive {
true => {
@@ -376,67 +178,13 @@ where
}
}
fn num_range_validate<T>(
minimum: Option<T>,
maximum: Option<T>,
default: T,
lower_inclusive: bool,
upper_inclusive: bool,
step: Option<T>,
value: T,
) -> bool
where
T: SimpleRangeItem,
{
if let (Some(step), Some(min)) = (step, minimum) {
let prepared_value: T = value - min;
// We can check the step if we subtract the value from the minimum value
// then see if the remainder of prepared value and step is zero.
// e.g. 4, 12, value is 7, step is 3
// 7 - 4 = 3
// 3 % 3 = 0 Valid!
if prepared_value % step != T::ZERO {
return false
}
}
if value == default {
return true
}
if let Some(min) = minimum {
let test = if lower_inclusive {
min <= value
} else {
min < value
};
if test {
return false
}
}
if let Some(max) = maximum {
let test = if upper_inclusive {
max >= value
} else {
max > value
};
if test {
return false
}
}
true
}
pub trait SimpleRangeItem: Copy + Clone + Debug + Div<Output = Self> + Sub<Output = Self> + Rem<Output = Self> + PartialOrd + PartialEq {
pub trait RangeItem: Copy + Clone + Debug + Div<Output = Self> + Sub<Output = Self> + Rem<Output = Self> + Hash + Ord + PartialOrd + Eq + PartialEq {
const ZERO: Self;
}
macro_rules! impl_num {
($($n:ty)*) => ($(
impl SimpleRangeItem for $n {
impl RangeItem for $n {
const ZERO: $n = 0;
}
)*)
@@ -444,10 +192,10 @@ macro_rules! impl_num {
impl_num! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 }
impl SimpleRangeItem for f32 {
const ZERO: Self = 0_f32;
impl RangeItem for OrderedFloat<f32> {
const ZERO: Self = OrderedFloat(0_f32);
}
impl SimpleRangeItem for f64 {
const ZERO: Self = 0_f64;
impl RangeItem for OrderedFloat<f64> {
const ZERO: Self = OrderedFloat(0_f64);
}
+9 -8
View File
@@ -1,9 +1,17 @@
use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_buffer::FrameBuffer;
use flume::{Receiver, TryRecvError};
use std::sync::Arc;
use derive_builder::Builder;
use flume::{Receiver, TryRecvError};
#[derive(Clone, Debug, Default, PartialEq, Builder)]
pub struct StreamConfiguration {
buffer_size: Option<u32>,
}
pub trait StreamInnerTrait {
fn configuration(&self) -> &StreamConfiguration;
fn receiver(&self) -> Arc<Receiver<FrameBuffer>>;
fn stop(&mut self) -> NokhwaResult<()>;
}
@@ -19,13 +27,6 @@ impl Stream {
}
}
// pub unsafe fn erase_lifetime(self) -> Stream<'static> {
// Self {
// inner: self.inner,
// phantom_data: Default::default(),
// }
// }
pub fn check_disconnected(&self) -> NokhwaResult<()> {
if self.inner.receiver().is_disconnected() {
return Err(NokhwaError::ReadFrameError(
+5 -4
View File
@@ -11,8 +11,8 @@ use std::{
};
use std::num::NonZeroI32;
use std::ops::{Div, Rem};
use num_rational::Rational32;
use crate::ranges::{SimpleRangeItem};
use num_rational::{Ratio, Rational32};
use crate::ranges::{RangeItem};
use num_traits::FromPrimitive;
/// Describes the index of the camera.
@@ -106,6 +106,7 @@ pub struct Resolution {
impl Resolution {
/// Create a new resolution from 2 image size coordinates.
#[must_use]
// TODO: make this height and width.
pub const fn new(x: u32, y: u32) -> Self {
Resolution {
width_x: x,
@@ -211,7 +212,7 @@ impl Rem for Resolution {
}
}
impl SimpleRangeItem for Resolution {
impl RangeItem for Resolution {
const ZERO: Self = Resolution::new(0, 0);
}
@@ -305,7 +306,7 @@ impl Rem for FrameRate {
}
}
impl SimpleRangeItem for FrameRate {
impl RangeItem for FrameRate {
const ZERO: Self = FrameRate::frame_rate(0);
}
+1 -1
View File
@@ -34,7 +34,7 @@ use nokhwa_core::{
use std::{ffi::CString, sync::Arc};
use std::{borrow::Cow, collections::HashMap};
use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
/// The backend struct that interfaces with V4L2.
/// To see what this does, please see [`CaptureTrait`].
+1 -1
View File
@@ -7,7 +7,7 @@ use serde::{de, Serialize};
use wasm_bindgen_futures::JsFuture;
use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator};
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_format::FrameFormat;
use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait};
+1 -1
View File
@@ -26,7 +26,7 @@ use nokhwa_core::{
},
};
use std::{borrow::Cow, collections::HashMap};
use nokhwa_core::properties::{all_known_camera_controls, CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{all_known_camera_controls, CameraControl, ControlValue, KnownCameraControl};
/// The backend that deals with Media Foundation on Windows.
/// To see what this does, please see [`CaptureTrait`].
+1 -1
View File
@@ -33,7 +33,7 @@ use opencv::{
},
};
use std::{borrow::Cow, collections::HashMap};
use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
/// Attempts to convert a [`KnownCameraControl`] into a `OpenCV` video capture property.
/// If the associated control is not found, this will return `Err`
+1 -1
View File
@@ -16,7 +16,7 @@ use nokhwa_core::{
camera::{Open, Setting},
error::{NokhwaError, NokhwaResult},
frame_format::FrameFormat,
properties::CameraProperties,
control::CameraProperties,
types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution}
};
+1 -1
View File
@@ -28,7 +28,7 @@ use nokhwa_core::{
},
};
use std::{borrow::Cow, collections::HashMap};
use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
/// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use.
pub struct Camera {
+2 -2
View File
@@ -31,7 +31,7 @@ use std::{
Arc, Mutex,
},
};
use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
type AtomicLock<T> = Arc<Mutex<T>>;
pub type CallbackFn = fn(
@@ -420,7 +420,7 @@ impl CallbackCamera {
/// Sets the control to `control` in the camera.
/// Usually, the pipeline is calling [`camera_control()`](crate::camera_traits::CaptureTrait::camera_control), getting a camera control that way
/// then calling [`value()`](nokhwa_core::properties::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::properties::ControlValue) and setting the value that way.
/// then calling [`value()`](nokhwa_core::control::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::control::ControlValue) and setting the value that way.
/// # Errors
/// If the `control` is not supported, the value is invalid (less than min, greater than max, not in step), or there was an error setting the control,
/// this will error.