mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
in progress v4l2 implementation, mostly stable core
This commit is contained in:
Generated
+309
-375
File diff suppressed because it is too large
Load Diff
+3
-12
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
@@ -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 {}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:?}")
|
||||
}
|
||||
}
|
||||
@@ -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}")]
|
||||
|
||||
@@ -63,11 +63,11 @@ pub enum FrameFormat {
|
||||
|
||||
// RGB Formats
|
||||
Rgb332,
|
||||
Rgb555,
|
||||
Rgb565,
|
||||
|
||||
Rgb888,
|
||||
|
||||
Bgr888,
|
||||
BgrA8888,
|
||||
|
||||
RgbA8888,
|
||||
ARgb8888,
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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`].
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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`].
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user