start fixing core

This commit is contained in:
l1npengtul
2024-12-01 04:18:51 +09:00
parent 7ad29e3e9c
commit 5d79e46d06
31 changed files with 1052 additions and 1255 deletions
Generated
+134 -99
View File
@@ -243,11 +243,11 @@ dependencies = [
[[package]]
name = "bit-set"
version = "0.6.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec 0.7.0",
"bit-vec 0.8.0",
]
[[package]]
@@ -258,9 +258,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bit-vec"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bit_field"
@@ -309,9 +309,9 @@ checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
[[package]]
name = "bytemuck"
version = "1.15.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
dependencies = [
"bytemuck_derive",
]
@@ -349,7 +349,7 @@ dependencies = [
"log",
"nix 0.25.1",
"slotmap",
"thiserror 1.0.63",
"thiserror 1.0.69",
"vec_map",
]
@@ -973,17 +973,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "d3d12"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017"
dependencies = [
"bitflags 2.6.0",
"libloading 0.8.3",
"winapi",
]
[[package]]
name = "darling"
version = "0.13.4"
@@ -1525,6 +1514,18 @@ dependencies = [
"web-sys",
]
[[package]]
name = "glow"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483"
dependencies = [
"js-sys",
"slotmap",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.5.0"
@@ -1627,22 +1628,21 @@ checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
dependencies = [
"log",
"presser",
"thiserror 1.0.63",
"thiserror 1.0.69",
"winapi",
"windows 0.52.0",
]
[[package]]
name = "gpu-allocator"
version = "0.26.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7"
checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
dependencies = [
"log",
"presser",
"thiserror 1.0.63",
"winapi",
"windows 0.52.0",
"thiserror 1.0.69",
"windows 0.58.0",
]
[[package]]
@@ -1721,7 +1721,7 @@ dependencies = [
"com",
"libc",
"libloading 0.8.3",
"thiserror 1.0.63",
"thiserror 1.0.69",
"widestring",
"winapi",
]
@@ -1763,7 +1763,7 @@ dependencies = [
"byteorder",
"color_quant",
"num-iter",
"num-rational",
"num-rational 0.3.2",
"num-traits",
]
@@ -1888,9 +1888,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"wasm-bindgen",
]
@@ -2288,7 +2288,7 @@ dependencies = [
"rustc-hash",
"spirv 0.2.0+1.5.4",
"termcolor",
"thiserror 1.0.63",
"thiserror 1.0.69",
"unicode-xid",
]
@@ -2309,18 +2309,18 @@ dependencies = [
"rustc-hash",
"spirv 0.3.0+sdk-1.3.268.0",
"termcolor",
"thiserror 1.0.63",
"thiserror 1.0.69",
"unicode-xid",
]
[[package]]
name = "naga"
version = "22.0.0"
version = "23.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09eeccb9b50f4f7839b214aa3e08be467159506a986c18e0702170ccf720a453"
checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e"
dependencies = [
"arrayvec 0.7.4",
"bit-set 0.6.0",
"bit-set 0.8.0",
"bitflags 2.6.0",
"cfg_aliases",
"codespan-reporting",
@@ -2330,7 +2330,7 @@ dependencies = [
"rustc-hash",
"spirv 0.3.0+sdk-1.3.268.0",
"termcolor",
"thiserror 1.0.63",
"thiserror 1.0.69",
"unicode-xid",
]
@@ -2363,7 +2363,7 @@ dependencies = [
"ndk-sys 0.4.1+23.1.7779620",
"num_enum",
"raw-window-handle 0.5.2",
"thiserror 1.0.63",
"thiserror 1.0.69",
]
[[package]]
@@ -2464,7 +2464,7 @@ dependencies = [
"rgb",
"serde",
"serde-wasm-bindgen",
"thiserror 1.0.63",
"thiserror 1.0.69",
"usb_enumeration",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -2512,16 +2512,25 @@ version = "0.2.0"
dependencies = [
"async-trait",
"bytes",
"dcv-color-primitives",
"flume 0.11.0",
"futures",
"image 0.25.0",
"mozjpeg",
"num-rational 0.4.2",
"opencv 0.93.1",
"paste",
"rgb",
"serde",
"thiserror 2.0.0",
"wgpu 22.0.0",
"wgpu 23.0.1",
]
[[package]]
name = "nokhwa-decoder"
version = "0.1.0"
dependencies = [
"dcv-color-primitives",
"mozjpeg",
"nokhwa-core",
"yuvutils-rs",
]
@@ -2548,6 +2557,16 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -2580,10 +2599,22 @@ dependencies = [
]
[[package]]
name = "num-traits"
version = "0.2.18"
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
"serde",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
@@ -2674,9 +2705,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "opencv"
@@ -3049,7 +3080,7 @@ checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
"getrandom",
"libredox",
"thiserror 1.0.63",
"thiserror 1.0.69",
]
[[package]]
@@ -3436,11 +3467,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.63"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.63",
"thiserror-impl 1.0.69",
]
[[package]]
@@ -3454,9 +3485,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "1.0.63"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
@@ -3651,9 +3682,9 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
version = "0.2.4"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "usb_enumeration"
@@ -3737,19 +3768,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
@@ -3762,9 +3794,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -3774,9 +3806,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3784,9 +3816,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
@@ -3797,9 +3829,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "wasm-rs-async-executor"
@@ -3885,9 +3917,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3949,16 +3981,16 @@ dependencies = [
[[package]]
name = "wgpu"
version = "22.0.0"
version = "23.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87e07e87a179614940ad845397e03201847453a37b43a31a3b54eee2e6e32ce"
checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a"
dependencies = [
"arrayvec 0.7.4",
"cfg_aliases",
"document-features",
"js-sys",
"log",
"naga 22.0.0",
"naga 23.0.0",
"parking_lot",
"profiling",
"raw-window-handle 0.6.0",
@@ -3967,9 +3999,9 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu-core 22.0.0",
"wgpu-hal 22.0.0",
"wgpu-types 22.0.0",
"wgpu-core 23.0.1",
"wgpu-hal 23.0.1",
"wgpu-types 23.0.0",
]
[[package]]
@@ -3990,7 +4022,7 @@ dependencies = [
"profiling",
"raw-window-handle 0.5.2",
"smallvec",
"thiserror 1.0.63",
"thiserror 1.0.69",
"web-sys",
"wgpu-hal 0.14.1",
"wgpu-types 0.14.1",
@@ -4017,7 +4049,7 @@ dependencies = [
"raw-window-handle 0.6.0",
"rustc-hash",
"smallvec",
"thiserror 1.0.63",
"thiserror 1.0.69",
"web-sys",
"wgpu-hal 0.20.0",
"wgpu-types 0.20.0",
@@ -4025,27 +4057,27 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "22.0.0"
version = "23.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f191908a21968991463fcf3b42cb6c9648c0fb7fa301b8fc733bc21a9ed9bd"
checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a"
dependencies = [
"arrayvec 0.7.4",
"bit-vec 0.7.0",
"bit-vec 0.8.0",
"bitflags 2.6.0",
"cfg_aliases",
"document-features",
"indexmap 2.2.6",
"log",
"naga 22.0.0",
"naga 23.0.0",
"once_cell",
"parking_lot",
"profiling",
"raw-window-handle 0.6.0",
"rustc-hash",
"smallvec",
"thiserror 1.0.63",
"wgpu-hal 22.0.0",
"wgpu-types 22.0.0",
"thiserror 1.0.69",
"wgpu-hal 23.0.1",
"wgpu-types 23.0.0",
]
[[package]]
@@ -4080,7 +4112,7 @@ dependencies = [
"raw-window-handle 0.5.2",
"renderdoc-sys 0.7.1",
"smallvec",
"thiserror 1.0.63",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types 0.14.1",
@@ -4125,7 +4157,7 @@ dependencies = [
"renderdoc-sys 1.1.0",
"rustc-hash",
"smallvec",
"thiserror 1.0.63",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types 0.20.0",
@@ -4134,32 +4166,31 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "22.0.0"
version = "23.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f"
checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821"
dependencies = [
"android_system_properties",
"arrayvec 0.7.4",
"ash 0.38.0+1.3.281",
"bit-set 0.6.0",
"bit-set 0.8.0",
"bitflags 2.6.0",
"block",
"bytemuck",
"cfg_aliases",
"core-graphics-types",
"d3d12 22.0.0",
"glow 0.13.1",
"glow 0.14.2",
"glutin_wgl_sys 0.6.0",
"gpu-alloc 0.6.0",
"gpu-allocator 0.26.0",
"gpu-allocator 0.27.0",
"gpu-descriptor 0.3.0",
"hassle-rs",
"js-sys",
"khronos-egl 6.0.0",
"libc",
"libloading 0.8.3",
"log",
"metal 0.29.0",
"naga 22.0.0",
"naga 23.0.0",
"ndk-sys 0.5.0+25.2.9519653",
"objc",
"once_cell",
@@ -4170,11 +4201,12 @@ dependencies = [
"renderdoc-sys 1.1.0",
"rustc-hash",
"smallvec",
"thiserror 1.0.63",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types 22.0.0",
"winapi",
"wgpu-types 23.0.0",
"windows 0.58.0",
"windows-core 0.58.0",
]
[[package]]
@@ -4199,9 +4231,9 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "22.0.0"
version = "23.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d"
checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068"
dependencies = [
"bitflags 2.6.0",
"js-sys",
@@ -4715,9 +4747,12 @@ checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
[[package]]
name = "yuvutils-rs"
version = "0.4.7"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b0c026d402c14683f21eae52b33dd76ba47c548273a7eb526eed5ce2f46b65"
checksum = "b30a62ce6c5fbf13dbf8d92e7cd805d74574a7f84d0e81462ca3cb8192be5de2"
dependencies = [
"num-traits",
]
[[package]]
name = "zerocopy"
+1 -1
View File
@@ -12,7 +12,7 @@ repository = "https://github.com/l1npengtul/nokhwa"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "examples/*"]
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "examples/*", "nokhwa-decoder"]
exclude = ["examples/jscam"]
[lib]
+2 -2
View File
@@ -34,14 +34,14 @@ use nokhwa::{
frame_formats, yuyv422_predicted_size, CameraFormat, CameraIndex, FrameFormat,
RequestedFormat, RequestedFormatType, Resolution,
},
Buffer, CallbackCamera, Camera,
FrameBuffer, CallbackCamera, Camera,
};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
struct CaptureState {
receiver: Arc<Receiver<Buffer>>,
receiver: Arc<Receiver<FrameBuffer>>,
buffer: Vec<u8>,
format: CameraFormat,
}
+2 -2
View File
@@ -1,4 +1,4 @@
use nokhwa_core::buffer::Buffer;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::pixel_format::RgbFormat;
use nokhwa_core::types::{FrameFormat, Resolution};
use std::fs::File;
@@ -11,7 +11,7 @@ fn main() {
.read_to_end(&mut nv12)
.unwrap();
let buffer = Buffer::new(Resolution::new(1920, 1080), &nv12, FrameFormat::NV12);
let buffer = FrameBuffer::new(Resolution::new(1920, 1080), &nv12, FrameFormat::NV12);
buffer
.decode_image::<RgbFormat>()
.unwrap()
+2 -1
View File
@@ -40,7 +40,8 @@
rustPlatform.bindgenHook
llvmPackages.libclang.lib
llvmPackages.clang
v4l-utils.override { withUtils = true; }
v4l-utils
libv4l
];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
shellHook = ''
+2 -2
View File
@@ -8,8 +8,8 @@ use v4l::frameinterval::FrameIntervalEnum;
use v4l::prelude::MmapStream;
use v4l::video::{Capture as V4lCapture, Output};
use v4l::video::output::Parameters;
use nokhwa_core::buffer::Buffer;
use nokhwa_core::capture::{Capture, Open, Setting, Stream};
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::error::{NokhwaError, NokhwaResult};
+9 -16
View File
@@ -14,10 +14,9 @@ repository = "https://github.com/l1npengtul/nokhwa"
default = []
serialize = ["serde"]
wgpu-types = ["wgpu"]
opencv-mat = ["opencv"]
opencv-mat = ["opencv", "opencv/clang-runtime"]
docs-features = ["serialize", "wgpu-types"]
conversions = ["dcv-color-primitives", "yuvutils-rs", "mozjpeg"]
async = ["async-trait"]
async = ["async-trait", "flume/async"]
test-fail-warnings = []
@@ -25,6 +24,12 @@ test-fail-warnings = []
thiserror = "2.0"
bytes = "1.3"
paste = "1.0"
flume = "0.11"
[dependencies.num-rational]
version = "0.4"
default-features = false
features = ["serde", "std"]
[dependencies.image]
version = "0.25"
@@ -36,7 +41,7 @@ features = ["derive"]
optional = true
[dependencies.wgpu]
version = "22"
version = "23"
optional = true
[dependencies.opencv]
@@ -44,22 +49,10 @@ version = "0.93"
default-features = false
optional = true
[dependencies.mozjpeg]
version = "0.10"
optional = true
[dependencies.async-trait]
version = "0.1"
optional = true
[dependencies.dcv-color-primitives]
version = "0.6"
optional = true
[dependencies.yuvutils-rs]
version = "0.4"
optional = true
[dependencies.futures]
version = "0.3"
optional = true
+132
View File
@@ -0,0 +1,132 @@
use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_buffer::FrameBuffer;
use crate::frame_format::FrameFormat;
use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue};
use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution};
use std::collections::HashMap;
use crate::stream::Stream;
pub trait Open {
fn open(index: CameraIndex) -> NokhwaResult<Self> where Self: Sized;
}
#[cfg(feature = "async")]
pub trait AsyncOpen: Sized {
async fn open_async(index: CameraIndex) -> NokhwaResult<Self>;
}
macro_rules! def_camera_props {
( $($property:ident, )* ) => {
paste::paste! {
$(
fn [<$property:snake>] (&self) -> Option<&crate::properties::CameraPropertyDescriptor> {
self.properties().[<$property:snake>]()
}
fn [<set_ $property:snake>] (&mut self, value: crate::properties::CameraPropertyValue) -> Result<(), crate::error::NokhwaError> {
self.set_property(&crate::properties::CameraPropertyId::$property, value)
}
)*
}
};
}
// macro_rules! def_camera_props_async {
// ( $($property:ident, )* ) => {
// paste::paste! {
// $(
// async fn [<set_ $property:snake _async>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
// self.[<set_ $property:snake >](value)
// }
// )*
// }
// };
// }
pub trait Setting {
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
fn enumerate_resolution_and_frame_rates(
&self,
frame_format: FrameFormat,
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
fn properties(&self) -> &CameraProperties;
fn set_property(
&mut self,
property: &CameraPropertyId,
value: CameraPropertyValue,
) -> Result<(), NokhwaError>;
def_camera_props!(
Brightness,
Contrast,
Hue,
Saturation,
Sharpness,
Gamma,
WhiteBalance,
BacklightCompensation,
Pan,
Tilt,
Zoom,
Exposure,
Iris,
Focus,
Facing,
);
}
// #[cfg(feature = "async")]
// pub trait AsyncSetting {
// async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
//
// async fn set_property_async(
// &mut self,
// property: &CameraPropertyId,
// value: CameraPropertyValue,
// ) -> Result<(), NokhwaError>;
//
// def_camera_props_async!(
// Brightness,
// Contrast,
// Hue,
// Saturation,
// Sharpness,
// Gamma,
// WhiteBalance,
// BacklightCompensation,
// Pan,
// Tilt,
// Zoom,
// Exposure,
// Iris,
// Focus,
// Facing,
// );
// }
pub trait Capture {
fn open_stream(&mut self) -> Result<Stream, NokhwaError>;
fn close_stream(&mut self) -> Result<(), NokhwaError>;
}
#[cfg(feature = "async")]
pub trait AsyncStream {
async fn open_stream(&mut self) -> Result<(), NokhwaError>;
async fn await_frame(&mut self) -> Result<FrameBuffer, NokhwaError>;
async fn close_stream(&mut self) -> Result<(), NokhwaError>;
}
pub trait CameraVtable: Setting + Capture {}
pub trait Camera: Open + CameraVtable {}
#[cfg(feature = "async")]
pub trait AsyncCapture: Camera + AsyncOpen + AsyncStream {}
-122
View File
@@ -1,122 +0,0 @@
use std::collections::HashMap;
use crate::buffer::Buffer;
use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue};
use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_format::FrameFormat;
use crate::stream::CaptureStream;
use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution};
pub trait Open {
fn open(index: CameraIndex) -> NokhwaResult<Self>;
}
#[cfg(feature = "async")]
pub trait AsyncOpen {
async fn open_async(index: CameraIndex) -> NokhwaResult<Self>;
}
macro_rules! def_camera_props {
( $($property:ident, )* ) => {
paste::paste! {
$(
fn [<$property:snake>] (&self) -> Option<&CameraPropertyDescriptor> {
self.properties().[<$property:snake>]
}
fn [<set_ $property:snake>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.properties().[<set_ $property:snake >](value)
}
)*
}
};
}
macro_rules! def_camera_props_async {
( $($property:ident, )* ) => {
paste::paste! {
$(
async fn [<set_ $property:snake _async>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.properties().[<set_ $property:snake >](value)
}
)*
}
};
}
pub trait Setting {
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
fn enumerate_resolution_and_frame_rates(&self, frame_format: FrameFormat) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
fn properties(&self) -> &CameraProperties;
fn set_property(&mut self, property: &CameraPropertyId, value: CameraPropertyValue) -> Result<(), NokhwaError>;
def_camera_props!(
Brightness,
Contrast,
Hue,
Saturation,
Sharpness,
Gamma,
WhiteBalance,
BacklightCompensation,
Gain,
Pan,
Tilt,
Zoom,
Exposure,
Iris,
Focus,
Facing,
);
}
#[cfg(feature = "async")]
pub trait AsyncSetting {
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn set_property_async(&mut self, property: &CameraPropertyId, value: CameraPropertyValue) -> Result<(), NokhwaError>;
def_camera_props_async!(
Brightness,
Contrast,
Hue,
Saturation,
Sharpness,
Gamma,
WhiteBalance,
BacklightCompensation,
Gain,
Pan,
Tilt,
Zoom,
Exposure,
Iris,
Focus,
Facing,
);
}
pub trait Stream {
fn open_stream(&mut self) -> Result<CaptureStream, NokhwaError>;
fn close_stream(&mut self) -> Result<(), NokhwaError>;
}
#[cfg(feature = "async")]
pub trait AsyncStream {
async fn open_stream(&mut self) -> Result<(), NokhwaError>;
async fn await_frame(&mut self) -> Result<Buffer, NokhwaError>;
async fn close_stream(&mut self) -> Result<(), NokhwaError>;}
pub trait Capture: Open + Setting + Stream {}
#[cfg(feature = "async")]
pub trait AsyncCapture: Capture + AsyncOpen + AsyncSetting + AsyncStream {}
+104
View File
@@ -0,0 +1,104 @@
use crate::{error::NokhwaError, frame_buffer::FrameBuffer, frame_format::FrameFormat};
use image::{ImageBuffer, Pixel};
use std::{
error::Error,
fmt::{Debug, Display},
ops::{ControlFlow, Deref},
};
/// Trait to define a struct that can decode a [`FrameBuffer`]
pub trait Decoder {
/// Formats that the decoder can decode.
const ALLOWED_FORMATS: &'static [FrameFormat];
/// Output pixel type (e.g. [`Rgb<u8>`](image::Rgb))
type OutputPixels: Pixel;
/// Container type for the decoder. Will be used for ImageBuffer
type PixelContainer: Deref<Target = [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]>;
fn check_format(buffer: &FrameBuffer) -> ControlFlow<NokhwaError> {
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
return ControlFlow::Break(NokhwaError::ConversionError("unsupported".to_string()));
}
ControlFlow::Continue(())
}
/// Decode function.
fn decode(
&mut self,
buffer: &FrameBuffer,
) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, NokhwaError>;
/// Decode to user-provided Buffer
///
/// Incase that the buffer is not large enough this should error.
fn decode_buffer(
&mut self,
buffer: &FrameBuffer,
output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel],
) -> Result<(), NokhwaError>;
/// Decoder Predicted Size
fn predicted_size_of_frame(buffer: &FrameBuffer) -> Option<usize> {
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
return None;
}
let res = buffer.resolution();
Some(
res.x() as usize
* res.y() as usize
* size_of::<<<Self as Decoder>::OutputPixels as Pixel>::Subpixel>()
* <<Self as Decoder>::OutputPixels as Pixel>::CHANNEL_COUNT as usize,
)
}
}
/// Decoder that can be used statically (struct contains no state)
///
/// This is useful for times that a simple function is all that is required.
pub trait StaticDecoder: Decoder {
fn decode_static(
buffer: &FrameBuffer,
) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, NokhwaError>;
fn decode_static_to_buffer(
buffer: &FrameBuffer,
output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel],
) -> Result<(), NokhwaError>;
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncDecoder: Decoder {
/// Asynchronous decoder
async fn decode_async(
&mut self,
buffer: &FrameBuffer,
) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, NokhwaError>;
/// Asynchronous decoder to user buffer.
async fn decode_buffer(
&mut self,
buffer: &FrameBuffer,
output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel],
) -> Result<(), NokhwaError>;
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncStaticDecoder: Decoder {
/// Asynchronous decoder
async fn decode_static_async(
buffer: &FrameBuffer,
) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, NokhwaError>;
/// Asynchronous decoder to user buffer.
async fn decode_static_buffer(
&mut self,
buffer: &FrameBuffer,
output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel],
) -> Result<(), NokhwaError>;
}
// #[cfg(feature = "decoders")]
-60
View File
@@ -1,60 +0,0 @@
use dcv_color_primitives::PixelFormat;
use image::{ExtendedColorType, ImageBuffer, Pixel, PixelWithColorType};
use crate::buffer::Buffer;
use crate::decoders::Decoder;
use crate::error::NokhwaError;
use crate::frame_format::FrameFormat;
pub struct GeneralPurposeDecoder<D> where D: PixelWithColorType;
impl<D> Decoder for GeneralPurposeDecoder<D> where D: PixelWithColorType {
const ALLOWED_FORMATS: &'static [FrameFormat] = &[
FrameFormat::MJpeg, FrameFormat::Luma8, FrameFormat::Luma16, FrameFormat::Rgb332, FrameFormat::RgbA8888,
FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Uyvy422, FrameFormat::Yuy2_422, FrameFormat::Yv12,
FrameFormat::Ayuv444, FrameFormat::I420, FrameFormat::I422, FrameFormat::I444
];
type OutputPixels = D;
type PixelContainer = Vec<D::Subpixel>;
type Error = NokhwaError;
fn decode(&mut self, buffer: Buffer) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, Self::Error> {
todo!()
}
fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error> {
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
return Err(NokhwaError::ConversionError(format!("Invaid frame format {} (allowed formats: {:?})", buffer.source_frame_format(), Self::ALLOWED_FORMATS)))
}
let destination = match D::COLOR_TYPE {
ExtendedColorType::Rgb8 => PixelFormat::Rgb,
ExtendedColorType::Rgba8 => PixelFormat::Rgba,
ExtendedColorType::Bgr8 => PixelFormat::Bgr,
ExtendedColorType::Bgra8 => PixelFormat::Bgra,
_ => return Err(())
};
// some extra processing needed for some formats
let source = match buffer.source_frame_format() {
FrameFormat::MJpeg => PixelFormat::Rgb, // => JPEG decoder
FrameFormat::Yuy2_422 => PixelFormat::I422,
FrameFormat::Uyvy422 => PixelFormat::I422,
FrameFormat::Ayuv444 => PixelFormat::I444,
FrameFormat::Nv12 => PixelFormat::Nv12,
FrameFormat::Nv21 => PixelFormat::Nv12,
FrameFormat::Yv12 => PixelFormat::I420,
FrameFormat::I420 => PixelFormat::I420,
// already decoded
FrameFormat::Rgb332 => PixelFormat::Rgb,
FrameFormat::RgbA8888 => {
PixelFormat::Rgb
}
_ => return Err(()),
};
dcv_color_primitives::convert_image(buffer.resolution().width(), buffer.resolution().height(), )
}
}
-67
View File
@@ -1,67 +0,0 @@
use std::ops::Deref;
use image::{ExtendedColorType, ImageBuffer, Pixel};
use crate::buffer::Buffer;
use crate::error::NokhwaError;
use crate::frame_format::FrameFormat;
/// Trait to define a struct that can decode a [`Buffer`]
pub trait Decoder {
/// Formats that the decoder can decode.
const ALLOWED_FORMATS: &'static [FrameFormat];
/// Output pixel type (e.g. [`Rgb<u8>`](image::Rgb))
type OutputPixels: Pixel;
type PixelContainer: Deref<Target = [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]>;
/// Error that the decoder will output (use [`NokhwaError`] if you're not sure)
type Error;
/// Decode function.
fn decode(&mut self, buffer: &Buffer) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, Self::Error>;
/// Decode to user-provided Buffer
///
/// Incase that the buffer is not large enough this should error.
fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>;
/// Decoder Predicted Size
fn predicted_size_of_frame(buffer: &Buffer) -> Option<usize> {
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
return None
}
let res = buffer.resolution();
Some(res.x() as usize * res.y() as usize * core::mem::size_of::<<<Self as Decoder>::OutputPixels as Pixel>::Subpixel>() * <<Self as Decoder>::OutputPixels as Pixel>::CHANNEL_COUNT as usize)
}
}
/// Decoder that can be used statically (struct contains no state)
///
/// This is useful for times that a simple function is all that is required.
pub trait StaticDecoder: Decoder {
fn decode_static(buffer: &Buffer) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, Self::Error>;
fn decode_static_to_buffer(&mut self, buffer: &Buffer, output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>;
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncDecoder: Decoder {
/// Asynchronous decoder
async fn decode_async(&mut self, buffer: &Buffer) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, Self::Error>;
/// Asynchronous decoder to user buffer.
async fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>;
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncStaticDecoder: Decoder {
/// Asynchronous decoder
async fn decode_static_async(buffer: &Buffer) -> Result<ImageBuffer<Self::OutputPixels, Self::PixelContainer>, Self::Error>;
/// Asynchronous decoder to user buffer.
async fn decode_static_buffer(&mut self, buffer: &Buffer, output: &mut [<<Self as Decoder>::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>;
}
#[cfg(feature = "conversions")]
pub mod general;
+1 -1
View File
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::{frame_format::FrameFormat, types::ApiBackend};
use std::fmt::{Debug};
use thiserror::Error;
pub type NokhwaResult<T> = Result<T, NokhwaError>;
+86 -47
View File
@@ -1,13 +1,10 @@
use std::{
cmp::Ordering,
collections::VecDeque
};
use crate::utils::Distance;
use crate::{
frame_format::FrameFormat,
traits::Distance,
types::{CameraFormat, FrameRate, Resolution}
ranges::Range,
types::{CameraFormat, FrameRate, Resolution},
};
use crate::ranges::Range;
use std::cmp::Ordering;
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
enum ClosestType {
@@ -25,7 +22,12 @@ pub enum CustomFormatRequestType {
Exact,
}
/// A helper for choosing a [`CameraFormat`].
/// The use of this is completely optional - for a simpler way try [`crate::camera::Camera::enumerate_formats`].
///
/// The `frame_format` field filters out the [`CameraFormat`]s by [`FrameFormat`].
pub enum FormatRequest {
/// Pick the closest [`CameraFormat`] to the one requested
Closest {
resolution: Option<Range<Resolution>>,
frame_rate: Option<Range<FrameRate>>,
@@ -47,51 +49,88 @@ pub enum FormatRequest {
}
impl FormatRequest {
pub fn sort_formats(&self, list_of_formats: &[CameraFormat]) -> Vec<CameraFormat> {
if list_of_formats.is_empty() {
return vec![];
}
match self {
FormatRequest::Closest {
resolution,
frame_rate,
frame_format,
} => {
let resolution_point = resolution.map(|x| x.preferred())?;
let frame_rate_point = frame_rate.map(|x| x.preferred())?;
// lets calcuate distance in 3 dimensions (add both resolution and frame_rate together)
let mut distances: Vec<(f32, CameraFormat)> = list_of_formats
.iter()
.filter(|x| frame_format.contains(&x.format()))
.map(|fmt| {
(
(fmt.frame_rate() - frame_rate_point).abs()
+ fmt.resolution().distance_from(&resolution_point) as f32,
fmt,
)
})
.collect::<Vec<_>>();
distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
distances.into_iter().map(|x| x.1).collect()
}
FormatRequest::HighestFrameRate {
frame_rate,
frame_format,
} => {
let mut formats = list_of_formats
.iter()
.filter(|x| {
frame_format.contains(&x.format()) && frame_rate.in_range(x.frame_rate())
})
.collect::<Vec<_>>();
formats.sort();
formats.into_iter().copied().collect()
}
FormatRequest::HighestResolution {
resolution,
frame_format,
} => {
let mut formats = list_of_formats
.iter()
.filter(|x| {
frame_format.contains(&x.format()) && resolution.in_range(x.resolution())
})
.collect::<Vec<_>>();
formats.sort();
formats.into_iter().copied().collect()
}
FormatRequest::Exact {
resolution,
frame_rate,
frame_format,
} => {
let mut formats = list_of_formats
.iter()
.filter(|x| {
frame_format.contains(&x.format())
&& resolution == &x.resolution()
&& frame_rate == &x.frame_rate()
})
.collect::<Vec<_>>();
formats.sort();
formats.into_iter().copied().collect()
}
}
}
///
#[must_use]
pub fn resolve(&self, list_of_formats: &[CameraFormat]) -> Option<CameraFormat> {
if list_of_formats.is_empty() {
return None;
}
match self {
FormatRequest::Closest { resolution, frame_rate, frame_format } => {
let resolution_point = resolution.map(|x| x.preferred())?;
let frame_rate_point = frame_rate.map(|x| x.preferred())?;
// lets calcuate distance in 3 dimensions (add both resolution and frame_rate together)
let mut distances = list_of_formats.iter()
.filter(|x| {
frame_format.contains(&x.format())
})
.map(|fmt| {
((fmt.frame_rate() - frame_rate_point).abs() + fmt.resolution().distance_from(&resolution_point) as f32, fmt)
})
.collect::<Vec<_>>();
distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
VecDeque::from(distances).pop_front().map(|x| x.1).copied()
}
FormatRequest::HighestFrameRate { frame_rate, frame_format } => {
let mut formats = list_of_formats.iter().filter(|x| {
frame_format.contains(&x.format()) && frame_rate.in_range(x.frame_rate())
}).collect::<Vec<_>>();
formats.sort();
formats.first().copied().copied()
}
FormatRequest::HighestResolution { resolution, frame_format } => {
let mut formats = list_of_formats.iter().filter(|x| {
frame_format.contains(&x.format()) && resolution.in_range(x.resolution())
}).collect::<Vec<_>>();
formats.sort();
formats.first().copied().copied()
}
FormatRequest::Exact { resolution, frame_rate, frame_format } => {
let mut formats = list_of_formats.iter().filter(|x| {
frame_format.contains(&x.format()) && resolution == &x.resolution() && frame_rate == &x.frame_rate()
}).collect::<Vec<_>>();
formats.sort();
formats.first().copied().copied()
}
}
Some(self.sort_formats(list_of_formats).remove(0))
}
}
@@ -14,22 +14,22 @@
* limitations under the License.
*/
use crate::frame_format::FrameFormat;
use crate::types::Resolution;
use bytes::Bytes;
use crate::frame_format::FrameFormat;
/// A buffer returned by a camera to accommodate custom decoding.
/// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer.
///
/// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames.
#[derive(Clone, Debug, Hash, PartialOrd, PartialEq, Eq)]
pub struct Buffer {
pub struct FrameBuffer {
resolution: Resolution,
buffer: Bytes,
source_frame_format: FrameFormat,
}
impl Buffer {
impl FrameBuffer {
/// Creates a new buffer with a [`&[u8]`].
#[must_use]
#[inline]
-5
View File
@@ -16,8 +16,6 @@
use std::fmt::{Display, Formatter};
use crate::types::ApiBackend;
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
@@ -40,7 +38,6 @@ pub enum FrameFormat {
// YCbCr Formats
// 8 bit per pixel, 4:4:4
Ayuv444,
// -> 4:2:2
@@ -49,14 +46,12 @@ pub enum FrameFormat {
Yvyu422,
Yv12,
// 4:2:0
Nv12,
Nv21,
I420,
// 16:1:1
Yvu9,
// Grayscale Formats
+6 -8
View File
@@ -22,18 +22,16 @@
*/
//! Core type definitions for `nokhwa`
pub mod buffer;
pub mod camera;
pub mod decoder;
pub mod error;
pub mod format_request;
pub mod frame_buffer;
pub mod frame_format;
pub mod properties;
pub mod query;
pub mod ranges;
pub mod traits;
pub mod types;
pub mod decoders;
pub mod utils;
pub mod ranges;
pub mod properties;
pub mod capture;
pub mod query;
#[cfg(feature = "async")]
pub mod stream;
+175 -179
View File
@@ -1,11 +1,11 @@
use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
collections::{HashMap, HashSet}
};
use std::cmp::Ordering;
use crate::error::NokhwaError;
use crate::ranges::{ArrayRange, IndicatedRange, KeyValue, Options, RangeValidationFailure, Simple, ValidatableRange};
use crate::ranges::{ArrayRange, KeyValue, Options, Range, RangeValidationFailure, Simple, ValidatableRange};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::{
collections::{HashMap, HashSet},
fmt::{Display, Formatter},
};
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ControlValidationFailure;
@@ -34,7 +34,13 @@ pub enum CameraPropertyId {
Iris,
Focus,
Facing,
Custom(String)
Custom(String),
}
impl Display for CameraPropertyId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
// TODO: Replace Controls API with Properties. (this one)
@@ -56,7 +62,7 @@ macro_rules! def_camera_props {
}
pub fn [<set_ $property:snake>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.props.set_property(&CameraPropertyId::$property, value)
self.set_property(&CameraPropertyId::$property, value)
}
)*
}
@@ -88,19 +94,21 @@ impl CameraProperties {
self.props.get(property)
}
pub fn set_property(&mut self, property: &CameraPropertyId, value: CameraPropertyValue) -> Result<(), NokhwaError> {
pub fn set_property(
&mut self,
property: &CameraPropertyId,
value: CameraPropertyValue,
) -> Result<(), NokhwaError> {
match self.props.get_mut(property) {
Some(prop) => {
Some(prop) => {
prop.set_value(value)?;
Ok(())
}
None => {
Err(NokhwaError::SetPropertyError {
property: property.to_string(),
value: value.to_string(),
error: String::from("Is null."),
})
}
None => Err(NokhwaError::SetPropertyError {
property: property.to_string(),
value: value.to_string(),
error: String::from("Is null."),
}),
}
}
}
@@ -116,10 +124,20 @@ pub struct CameraPropertyDescriptor {
}
impl CameraPropertyDescriptor {
pub fn new(flags: &[CameraPropertyFlag], mode: CameraPropertyMode, range: CameraPropertyRange, value: CameraPropertyValue, value_type: CameraPropertyValueType) -> Result<Self, NokhwaError> {
if flags.contains(&CameraPropertyFlag::ReadOnly) && flags.contains(&CameraPropertyFlag::WriteOnly) {
return Err(NokhwaError::StructureError { structure: "CameraPropertyDescriptor".to_string(), error: "conflicting flags".to_string() })
pub fn new(
flags: &[CameraPropertyFlag],
mode: CameraPropertyMode,
range: CameraPropertyRange,
value: CameraPropertyValue,
value_type: CameraPropertyValueType,
) -> Result<Self, NokhwaError> {
if flags.contains(&CameraPropertyFlag::ReadOnly)
&& flags.contains(&CameraPropertyFlag::WriteOnly)
{
return Err(NokhwaError::StructureError {
structure: "CameraPropertyDescriptor".to_string(),
error: "conflicting flags".to_string(),
});
}
Ok(CameraPropertyDescriptor {
@@ -141,7 +159,10 @@ impl CameraPropertyDescriptor {
pub fn is_disabled(&self) -> Result<(), NokhwaError> {
if self.flags.contains(&CameraPropertyFlag::Disabled) {
return Err(NokhwaError::StructureError { structure: "CameraPropertyDescriptor".to_string(), error: "Disabled".to_string() })
return Err(NokhwaError::StructureError {
structure: "CameraPropertyDescriptor".to_string(),
error: "Disabled".to_string(),
});
}
Ok(())
}
@@ -168,11 +189,13 @@ impl CameraPropertyDescriptor {
}
pub fn set_value(&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.range.check_value(&value).map_err(|_| NokhwaError::SetPropertyError {
property: "CameraPropertyValue".to_string(),
value: value.to_string(),
error: "Bad Type".to_string(),
})?;
self.range
.check_value(&value)
.map_err(|_| NokhwaError::SetPropertyError {
property: "CameraPropertyValue".to_string(),
value: value.to_string(),
error: "Bad Type".to_string(),
})?;
self.value = value;
Ok(())
}
@@ -239,18 +262,27 @@ impl Display for CameraPropertyFlag {
pub enum CameraPropertyRange {
Null,
Boolean(Simple<bool>),
Integer(IndicatedRange<i64>),
LongInteger(IndicatedRange<i128>),
Float(IndicatedRange<f32>),
Double(IndicatedRange<f64>),
Integer(Range<i64>),
LongInteger(Range<i128>),
Float(Range<f32>),
Double(Range<f64>),
String(Simple<String>),
Array(ArrayRange<Vec<CameraPropertyValue>>),
Enumeration(Options<CameraPropertyValue>),
Binary(Simple<Vec<u8>>),
Pair(IndicatedRange<f32>, IndicatedRange<f32>),
Triple(IndicatedRange<f32>, IndicatedRange<f32>, IndicatedRange<f32>),
Quadruple(IndicatedRange<f32>, IndicatedRange<f32>, IndicatedRange<f32>, IndicatedRange<f32>),
KeyValuePair(KeyValue<String, CameraPropertyValue>)
Pair(Range<f32>, Range<f32>),
Triple(
Range<f32>,
Range<f32>,
Range<f32>,
),
Quadruple(
Range<f32>,
Range<f32>,
Range<f32>,
Range<f32>,
),
KeyValuePair(KeyValue<String, CameraPropertyValue>),
}
impl CameraPropertyRange {
@@ -258,7 +290,7 @@ impl CameraPropertyRange {
match self {
CameraPropertyRange::Null => {
if let CameraPropertyValue::Null = value {
return Ok(())
return Ok(());
}
}
CameraPropertyRange::Boolean(chk_b) => {
@@ -331,7 +363,7 @@ impl CameraPropertyRange {
if let CameraPropertyValue::KeyValue(st, va) = value {
if let Some(vk) = kv.by_key(st) {
if vk.is_same_type(va) {
return Ok(())
return Ok(());
}
}
}
@@ -361,7 +393,7 @@ pub enum CameraPropertyValue {
Pair(f32, f32),
Triple(f32, f32, f32),
Quadruple(f32, f32, f32, f32),
KeyValue(String, Box<CameraPropertyValue>)
KeyValue(String, Box<CameraPropertyValue>),
}
impl CameraPropertyValue {
@@ -441,22 +473,22 @@ impl PartialEq for CameraPropertyValue {
}
CameraPropertyValue::Pair(a, b) => {
if let CameraPropertyValue::Pair(oa, ob) = other {
return (a == oa) && (b == ob)
return (a == oa) && (b == ob);
}
}
CameraPropertyValue::Triple(x, y, z) => {
if let CameraPropertyValue::Triple(ox, oy, oz) = other {
return (x == ox) && (y == oy) && (z == oz)
return (x == ox) && (y == oy) && (z == oz);
}
}
CameraPropertyValue::Quadruple(x, y, z, w) => {
if let CameraPropertyValue::Quadruple(ox, oy, oz, ow) = other {
return (x == ox) && (y == oy) && (z == oz) && (w == ow)
return (x == ox) && (y == oy) && (z == oz) && (w == ow);
}
}
CameraPropertyValue::KeyValue(k, v) => {
if let CameraPropertyValue::KeyValue(ok, ov ) = other {
return (k == ok) && (v == ov)
if let CameraPropertyValue::KeyValue(ok, ov) = other {
return (k == ok) && (v == ov);
}
}
_ => {}
@@ -468,145 +500,109 @@ impl PartialEq for CameraPropertyValue {
impl PartialOrd for CameraPropertyValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self {
CameraPropertyValue::Null => {
match other {
CameraPropertyValue::Null => Some(Ordering::Greater),
_ => Some(Ordering::Less)
CameraPropertyValue::Null => match other {
CameraPropertyValue::Null => Some(Ordering::Greater),
_ => Some(Ordering::Less),
},
CameraPropertyValue::Boolean(b) => match other {
CameraPropertyValue::Null => Some(Ordering::Greater),
CameraPropertyValue::Boolean(o) => {
if o == b {
Some(Ordering::Equal)
} else if o {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
}
CameraPropertyValue::Boolean(b) => {
match other {
CameraPropertyValue::Null => Some(Ordering::Greater),
CameraPropertyValue::Boolean(o) => {
if o == b {
Some(Ordering::Equal)
} else if o {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
}
}
_ => Some(Ordering::Less)
_ => Some(Ordering::Less),
},
CameraPropertyValue::Integer(int) => match other {
CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => {
Some(Ordering::Greater)
}
}
CameraPropertyValue::Integer(int) => {
match other {
CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => Some(Ordering::Greater),
CameraPropertyValue::Integer(oth) => {
Some(int.cmp(oth))
}
CameraPropertyValue::LongInteger(li) => {
let long = match i64::try_from(li) {
Ok(v) => v,
Err(_) => return None,
};
Some(int.cmp(&long))
}
_ => Some(Ordering::Less),
CameraPropertyValue::Integer(oth) => Some(int.cmp(oth)),
CameraPropertyValue::LongInteger(li) => {
let long = match i64::try_from(li) {
Ok(v) => v,
Err(_) => return None,
};
Some(int.cmp(&long))
}
}
CameraPropertyValue::LongInteger(long) => {
match other {
CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => Some(Ordering::Greater),
CameraPropertyValue::Integer(oth) => {
Some(long.cmp(&(i128::from(oth))))
}
CameraPropertyValue::LongInteger(o) => {
Some(long.cmp(o))
}
_ => Some(Ordering::Less),
_ => Some(Ordering::Less),
},
CameraPropertyValue::LongInteger(long) => match other {
CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => {
Some(Ordering::Greater)
}
}
CameraPropertyValue::Float(fl) => {
match other {
CameraPropertyValue::Null |
CameraPropertyValue::Boolean(_) |
CameraPropertyValue::Integer(_) |
CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater),
CameraPropertyValue::Float(f) => {
fl.partial_cmp(f)
}
CameraPropertyValue::Double(d) => {
f64::from(fl).partial_cmp(d)
}
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::Double(d) => {
match other {
CameraPropertyValue::Null |
CameraPropertyValue::Boolean(_) |
CameraPropertyValue::Integer(_) |
CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater),
CameraPropertyValue::Float(f) => {
d.partial_cmp(&(f64::from(f)))
}
CameraPropertyValue::Double(o) => {
d.partial_cmp(o)
}
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::String(s) => {
match other {
CameraPropertyValue::Null |
CameraPropertyValue::Boolean(_) |
CameraPropertyValue::Integer(_) |
CameraPropertyValue::LongInteger(_) |
CameraPropertyValue::Float(_) |
CameraPropertyValue::Double(_) => Some(Ordering::Greater),
CameraPropertyValue::String(os) => {
s.partial_cmp(os)
}
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::Array(a) => {
match other {
CameraPropertyValue::Null |
CameraPropertyValue::Boolean(_) |
CameraPropertyValue::Integer(_) |
CameraPropertyValue::LongInteger(_) |
CameraPropertyValue::Float(_) |
CameraPropertyValue::Double(_) |
CameraPropertyValue::String(_) => Some(Ordering::Greater),
CameraPropertyValue::Array(oa) => {
a.partial_cmp(oa)
}
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::EnumValue(_) => {
match other {
CameraPropertyValue::Null |
CameraPropertyValue::Boolean(_) |
CameraPropertyValue::Integer(_) |
CameraPropertyValue::LongInteger(_) |
CameraPropertyValue::Float(_) |
CameraPropertyValue::Double(_) |
CameraPropertyValue::String(_) |
CameraPropertyValue::Array(_) => Some(Ordering::Greater),
CameraPropertyValue::EnumValue(_) => Some(Ordering::Equal),
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::Binary(b) => {
match other {
CameraPropertyValue::Null|
CameraPropertyValue::Boolean(_)|
CameraPropertyValue::Integer(_)|
CameraPropertyValue::LongInteger(_)|
CameraPropertyValue::Float(_)|
CameraPropertyValue::Double(_)|
CameraPropertyValue::String(_)|
CameraPropertyValue::Array(_)|
CameraPropertyValue::EnumValue(_) => Some(Ordering::Greater),
CameraPropertyValue::Binary(ob) => {
b.partial_cmp(ob)
}
_ => Some(Ordering::Less),
}
}
CameraPropertyValue::Integer(oth) => Some(long.cmp(&(i128::from(oth)))),
CameraPropertyValue::LongInteger(o) => Some(long.cmp(o)),
_ => Some(Ordering::Less),
},
CameraPropertyValue::Float(fl) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater),
CameraPropertyValue::Float(f) => fl.partial_cmp(f),
CameraPropertyValue::Double(d) => f64::from(fl).partial_cmp(d),
_ => Some(Ordering::Less),
},
CameraPropertyValue::Double(d) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater),
CameraPropertyValue::Float(f) => d.partial_cmp(&(f64::from(f))),
CameraPropertyValue::Double(o) => d.partial_cmp(o),
_ => Some(Ordering::Less),
},
CameraPropertyValue::String(s) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_)
| CameraPropertyValue::Float(_)
| CameraPropertyValue::Double(_) => Some(Ordering::Greater),
CameraPropertyValue::String(os) => s.partial_cmp(os),
_ => Some(Ordering::Less),
},
CameraPropertyValue::Array(a) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_)
| CameraPropertyValue::Float(_)
| CameraPropertyValue::Double(_)
| CameraPropertyValue::String(_) => Some(Ordering::Greater),
CameraPropertyValue::Array(oa) => a.partial_cmp(oa),
_ => Some(Ordering::Less),
},
CameraPropertyValue::EnumValue(_) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_)
| CameraPropertyValue::Float(_)
| CameraPropertyValue::Double(_)
| CameraPropertyValue::String(_)
| CameraPropertyValue::Array(_) => Some(Ordering::Greater),
CameraPropertyValue::EnumValue(_) => Some(Ordering::Equal),
_ => Some(Ordering::Less),
},
CameraPropertyValue::Binary(b) => match other {
CameraPropertyValue::Null
| CameraPropertyValue::Boolean(_)
| CameraPropertyValue::Integer(_)
| CameraPropertyValue::LongInteger(_)
| CameraPropertyValue::Float(_)
| CameraPropertyValue::Double(_)
| CameraPropertyValue::String(_)
| CameraPropertyValue::Array(_)
| CameraPropertyValue::EnumValue(_) => Some(Ordering::Greater),
CameraPropertyValue::Binary(ob) => b.partial_cmp(ob),
_ => Some(Ordering::Less),
},
// FIXME: implement this lole
CameraPropertyValue::Pair(_, _) => {
// match other {
+164 -135
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap;
use core::fmt::{ Debug, Display, Formatter};
use std::collections::hash_map::Keys;
use std::hash::Hash;
use std::ops::{Div, Sub};
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)]
@@ -22,27 +22,25 @@ pub trait ValidatableRange {
///
/// Inclusive by default.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Range<T>
{
pub struct Range<T> {
minimum: Option<T>,
lower_inclusive: bool,
maximum: Option<T>,
upper_inclusive: bool,
preferred: T,
step: Option<T>,
}
impl<T> Range<T>
where
T: Copy + Clone + Debug + PartialOrd + PartialEq,
{
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>) -> Self {
pub fn new(preferred: T, min: Option<T>, max: Option<T>, step: Option<T>) -> Self {
Self {
minimum: min,
lower_inclusive: true,
maximum: max,
upper_inclusive: true,
preferred,
step,
}
}
@@ -52,6 +50,7 @@ where
lower_inclusive: bool,
max: Option<T>,
upper_inclusive: bool,
step: Option<T>
) -> Self {
Self {
minimum: min,
@@ -59,6 +58,7 @@ where
maximum: max,
upper_inclusive,
preferred,
step,
}
}
@@ -69,6 +69,7 @@ where
maximum: None,
upper_inclusive: true,
preferred,
step: None,
}
}
@@ -84,6 +85,9 @@ where
pub fn set_upper_inclusive(&mut self, upper_inclusive: bool) {
self.upper_inclusive = upper_inclusive;
}
pub fn set_step(&mut self, step: T) {
self.step = Some(step);
}
pub fn set_preferred(&mut self, preferred: T) {
self.preferred = preferred;
}
@@ -102,13 +106,27 @@ where
pub fn preferred(&self) -> T {
self.preferred
}
pub fn step(&self) -> Option<T> {
self.step
}
}
impl<T> ValidatableRange for Range<T> where T: PartialEq + PartialOrd {
impl<T> ValidatableRange for Range<T>
where
T: SimpleRangeItem,
{
type Validation = T;
fn validate(&self, value: Self::Validation) -> Result<(), RangeValidationFailure> {
num_range_validate(self.minimum.as_ref(), self.maximum.as_ref(), &self.preferred, self.lower_inclusive, self.upper_inclusive, &value)
fn validate(&self, value: &T) -> Result<(), RangeValidationFailure> {
num_range_validate(
self.minimum,
self.maximum,
self.preferred,
self.lower_inclusive,
self.upper_inclusive,
self.step,
*value,
)
}
}
@@ -123,107 +141,39 @@ where
maximum: None,
upper_inclusive: true,
preferred: T::default(),
step: None,
}
}
}
impl<T> Display for Range<T> where T: Debug {
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 = default_to_string(&self.preferred);
write!(f, "Range: {lower_inclusive_char}{}, {}{upper_inclusive_char}, Preferred: {default}", self.minimum, self.maximum)
}
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub struct IndicatedRange<T> where T: Copy + Clone + Debug + PartialOrd + PartialEq {
minimum: T,
lower_inclusive: bool,
maximum: T,
upper_inclusive: bool,
step: Option<T>,
default: Option<T>,
}
impl<T> IndicatedRange<T>
impl<T> Display for Range<T>
where
T: Copy + Clone + Debug + PartialOrd + PartialEq
T: Debug,
{
pub fn new(minimum: T, lower_inclusive: bool, maximum: T, upper_inclusive: bool, step: Option<T>, default: Option<T>) -> Self {
Self { minimum, lower_inclusive, maximum, upper_inclusive, step, default }
}
pub fn minimum(&self) -> T {
self.minimum
}
pub fn lower_inclusive(&self) -> bool {
self.lower_inclusive
}
pub fn maximum(&self) -> T {
self.maximum
}
pub fn upper_inclusive(&self) -> bool {
self.upper_inclusive
}
pub fn step(&self) -> Option<T> {
self.step
}
pub fn default_value(&self) -> Option<T> {
self.default
}
}
impl<T> ValidatableRange for IndicatedRange<T> where T: Copy + PartialEq + PartialOrd + Div<Output = T> + Sub<Output = T> + Number {
type Validation = T;
fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure> {
if let Some(step) = &self.step {
let prepared_value = value - &self.minimum;
// 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 != 0 {
return Err(RangeValidationFailure::default())
}
}
num_range_validate(self.minimum.as_ref(), self.maximum.as_ref(), &self.default, self.lower_inclusive, self.upper_inclusive, &value)
}
}
impl<T> Display for IndicatedRange<T> where T: Debug {
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 = default_to_string(&self.default);
let step = default_to_string(&self.step);
let default = &self.preferred;
// Ex) IndicatedRange: (5, 19], Step: 3, Default: 8
write!(f, "IndicatedRange: {lower_inclusive_char}{}, {}{upper_inclusive_char}, Step: {step}, Default: {default}", self.minimum, self.maximum)
write!(
f,
"Range: {lower_inclusive_char}{:?}, {:?}{upper_inclusive_char}, Preferred: {default:?}",
self.minimum, self.maximum
)
}
}
#[derive(Clone, Debug)]
pub struct Options<T> where T: Clone + Debug {
pub struct Options<T> {
default: Option<T>,
available: Vec<T>,
}
impl<T> Options<T>
where
T: Clone + Debug + PartialEq
T: Clone + Debug + PartialEq,
{
pub fn new(values: Vec<T>, default_value: T) -> Self {
pub fn new(values: Vec<T>, default_value: Option<T>) -> Self {
Self {
default: default_value,
available: values,
@@ -239,7 +189,10 @@ where
}
}
impl<T> ValidatableRange for Options<T> where T: PartialEq {
impl<T> ValidatableRange for Options<T>
where
T: Clone + PartialEq,
{
type Validation = T;
fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure> {
@@ -250,32 +203,41 @@ impl<T> ValidatableRange for Options<T> where T: PartialEq {
}
}
impl<T> Display for Options<T> where T: Debug {
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)
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 {
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
V: Clone + Debug,
{
pub fn new(default: HashMap<K, V>) -> Self {
Self {
defaults: default,
}
Self { defaults: default }
}
pub fn available_keys(&self) -> &Keys<'_, K, V> {
&self.defaults.keys()
pub fn available_keys(&self) -> Keys<'_, K, V> {
self.defaults.keys()
}
pub fn by_key(&self, key: &K) -> Option<&V> {
@@ -283,7 +245,11 @@ where
}
}
impl<K, V> Display for KeyValue<K, V> where K: Debug, V: Debug {
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)
@@ -291,16 +257,19 @@ impl<K, V> Display for KeyValue<K, V> where K: Debug, V: Debug {
}
#[derive(Clone, Debug)]
pub struct ArrayRange<T> where T: Clone + Debug {
pub struct ArrayRange<T> {
appendable_options: Vec<T>,
default_options: Vec<T>,
}
impl<T> ArrayRange<T> where T: Clone + Debug + PartialEq {
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() })
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() });
}
}
@@ -319,7 +288,10 @@ impl<T> ArrayRange<T> where T: Clone + Debug + PartialEq {
}
}
impl<T> ValidatableRange for ArrayRange<T> where T: PartialEq {
impl<T> ValidatableRange for ArrayRange<T>
where
T: PartialEq,
{
type Validation = T;
fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure> {
@@ -330,22 +302,30 @@ impl<T> ValidatableRange for ArrayRange<T> where T: PartialEq {
}
}
impl<T> Display for ArrayRange<T> where T: Debug {
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)
write!(
f,
"ArrayRange: Available Options: {:?}, Default: {:?}",
self.appendable_options, self.default_options
)
}
}
#[derive(Clone, Debug)]
pub struct Simple<T> where T: Clone + Debug {
default: Option<T>
pub struct Simple<T> {
default: Option<T>,
}
impl<T> Simple<T> where T: Clone + Debug {
impl<T> Simple<T>
where
T: Clone + Debug,
{
pub fn new(default: Option<T>) -> Self {
Self {
default,
}
Self { default }
}
pub fn default_value(&self) -> Option<&T> {
@@ -361,7 +341,10 @@ impl<T> ValidatableRange for Simple<T> {
}
}
impl<T> Display for Simple<T> where T: Debug {
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}")
@@ -370,12 +353,27 @@ impl<T> Display for Simple<T> where T: Debug {
fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char {
match inclusive {
true => if upper { ']' } else { '[' },
false => if upper { ')' } else { '(' },
true => {
if upper {
']'
} else {
'['
}
}
false => {
if upper {
')'
} else {
'('
}
}
}
}
fn default_to_string<T>(default: &Option<T>) -> String where T: Debug {
fn default_to_string<T>(default: &Option<T>) -> String
where
T: Debug,
{
match default {
Some(v) => {
format!("{v:?}")
@@ -384,10 +382,33 @@ fn default_to_string<T>(default: &Option<T>) -> String where T: Debug {
}
}
fn num_range_validate<T>(
minimum: Option<T>,
maximum: Option<T>,
default: T,
lower_inclusive: bool,
upper_inclusive: bool,
step: Option<T>,
value: T,
) -> Result<(), RangeValidationFailure>
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 Err(RangeValidationFailure::default());
}
}
fn num_range_validate<T>(minimum: Option<&T>, maximum: Option<&T>, default: &T, lower_inclusive: bool, upper_inclusive: bool, value: &T) -> Result<(), RangeValidationFailure> where T: PartialEq + PartialOrd {
if value == default {
return Ok(())
return Ok(());
}
if let Some(min) = minimum {
@@ -415,16 +436,24 @@ fn num_range_validate<T>(minimum: Option<&T>, maximum: Option<&T>, default: &T,
Ok(())
}
trait Number {}
macro_rules! impl_num {
( $($n:ty, )* ) => {
{
$(
impl Number for $n {}
)*
}
};
pub trait SimpleRangeItem: Copy + Clone + Debug + Div<Output = Self> + Sub<Output = Self> + Rem<Output = Self> + PartialOrd + PartialEq {
const ZERO: Self;
}
impl_num!( i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, f32, f64, );
macro_rules! impl_num {
($($n:ty)*) => ($(
impl SimpleRangeItem for $n {
const ZERO: $n = 0;
}
)*)
}
impl_num! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 }
impl SimpleRangeItem for f32 {
const ZERO: Self = 0_f32;
}
impl SimpleRangeItem for f64 {
const ZERO: Self = 0_f64;
}
+74 -31
View File
@@ -1,43 +1,86 @@
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll},
sync::mpsc::Receiver
};
use futures::Stream as AsyncStreamTrait;
use crate::{
buffer::Buffer,
error::NokhwaError
};
use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_buffer::FrameBuffer;
use flume::Receiver;
use futures::TryFutureExt;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub enum ChannelState {
Frame(Buffer),
Error(NokhwaError),
ClosedWithError(NokhwaError),
Closed,
pub trait StreamInnerTrait {
fn receiver(&self) -> Arc<Receiver<FrameBuffer>>;
fn stop(&mut self) -> NokhwaResult<()>;
}
pub enum StreamType {
Channel(Arc<Receiver<ChannelState>>),
Callback(Arc<Mutex<Option<ChannelState>>>),
pub struct Stream {
inner: Box<dyn StreamInnerTrait>,
}
pub struct CaptureStream {}
impl Stream {
pub fn new(inner: Box<dyn StreamInnerTrait>) -> Self {
Self {
inner,
}
}
impl Future for CaptureStream {
type Output = ChannelState;
// pub unsafe fn erase_lifetime(self) -> Stream<'static> {
// Self {
// inner: self.inner,
// phantom_data: Default::default(),
// }
// }
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
todo!()
pub fn poll_frame(&self) -> NokhwaResult<FrameBuffer> {
if self.inner.receiver().is_disconnected() {
return Err(NokhwaError::ReadFrameError(
"stream is disconnected!".to_string(),
));
}
self.inner
.receiver()
.recv()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))
}
pub fn try_poll_frame(&self) -> Option<NokhwaResult<FrameBuffer>> {
if self.inner.receiver().is_disconnected() {
return Some(Err(NokhwaError::ReadFrameError(
"stream is disconnected!".to_string(),
)));
}
if self.inner.receiver().is_empty() {
return None;
}
Some(
self.inner
.receiver()
.try_recv()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string())),
)
}
#[cfg(feature = "async")]
pub async fn await_frame(&self) -> NokhwaResult<FrameBuffer> {
if self.inner.receiver().is_disconnected() {
return Err(NokhwaError::ReadFrameError(
"stream is disconnected!".to_string(),
));
}
self.inner
.receiver()
.recv_async()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string())).await
}
pub fn stop_stream(mut self) -> NokhwaResult<()> {
self.inner.stop()?;
Ok(())
}
}
impl AsyncStreamTrait for CaptureStream {
type Item = ChannelState;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
impl Drop for Stream {
fn drop(&mut self) {
let _ = self.inner.stop();
}
}
-4
View File
@@ -15,7 +15,3 @@
*/
// pub trait VirtualBackendTrait {}
pub trait Distance<T> where T: PartialEq {
fn distance_from(&self, other: &Self) -> T;
}
+93 -419
View File
@@ -1,18 +1,18 @@
use crate::{
error::NokhwaError,
frame_format::FrameFormat,
};
use crate::utils::Distance;
use crate::{error::NokhwaError, frame_format::FrameFormat};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::{
borrow::Borrow, cmp::Ordering, fmt::{
Debug,
Display,
Formatter
}, hash::{Hash, Hasher}, ops::{Add, Deref, DerefMut, Sub}
borrow::Borrow,
cmp::Ordering,
fmt::{Debug, Display, Formatter},
hash::{Hash},
ops::{Sub},
};
use crate::traits::Distance;
use std::num::NonZeroI32;
use std::ops::{Div, Rem};
use num_rational::Rational32;
use crate::ranges::{SimpleRangeItem};
/// Describes the index of the camera.
/// - Index: A numbered index
@@ -105,7 +105,7 @@ pub struct Resolution {
impl Resolution {
/// Create a new resolution from 2 image size coordinates.
#[must_use]
pub fn new(x: u32, y: u32) -> Self {
pub const fn new(x: u32, y: u32) -> Self {
Resolution {
width_x: x,
height_y: y,
@@ -180,78 +180,123 @@ impl Distance<u32> for Resolution {
}
}
#[derive(Copy, Clone, Debug, Hash)]
impl Div for Resolution {
type Output = Resolution;
fn div(self, rhs: Self) -> Self::Output {
let x_div = self.x().div(rhs.x());
let y_div = self.y().div(rhs.y());
Resolution::new(x_div, y_div)
}
}
impl Sub for Resolution {
type Output = Resolution;
fn sub(self, rhs: Self) -> Self::Output {
let x_sub = self.x().sub(rhs.x());
let y_sub = self.y().sub(rhs.y());
Resolution::new(x_sub, y_sub)
}
}
impl Rem for Resolution {
type Output = Resolution;
fn rem(self, rhs: Self) -> Self::Output {
let x_rem = self.x().rem(rhs.x());
let y_rem = self.y().rem(rhs.y());
Resolution::new(x_rem, y_rem)
}
}
impl SimpleRangeItem for Resolution {
const ZERO: Self = Resolution::new(0, 0);
}
/// Framerate of a camera, backed by a num-rational Ratio type.
///
/// Note that while constructing negative is allowed, the absolute value
/// will be passed to the driver.
///
/// # Panics
/// If denominator is 0, any attempt to use the [`FrameRate`] will **panic.**
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct FrameRate {
numerator: u32,
denominator: u32
rational: Rational32,
}
impl FrameRate {
pub fn new(numerator: u32, denominator: u32) -> Self {
pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
Self {
numerator,
denominator,
rational: Rational32::new_raw(numerator, denominator.get()),
}
}
pub fn frame_rate(fps: u32) -> Self {
pub const fn frame_rate(fps: i32) -> Self {
Self {
numerator: fps,
denominator: 1,
rational: Rational32::new_raw(fps, 1),
}
}
pub fn numerator(&self) -> u32 {
self.numerator
pub fn numerator(&self) -> &i32 {
self.rational.numer()
}
pub fn denominator(&self) -> u32 {
self.denominator
pub fn denominator(&self) -> &i32 {
self.rational.denom()
}
}
impl Default for FrameRate {
fn default() -> Self {
FrameRate::new(30, 1)
FrameRate::new(30, NonZeroI32::new(1).unwrap())
}
}
impl Display for FrameRate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{} FPS", self.numerator, self.denominator)
write!(f, "{}/{} FPS", self.numerator(), self.denominator())
}
}
impl Eq for FrameRate {}
impl Div for FrameRate {
type Output = FrameRate;
impl PartialEq<Self> for FrameRate {
fn eq(&self, other: &Self) -> bool {
(self.numerator * other.denominator) == (other.numerator * self.numerator)
fn div(self, rhs: Self) -> Self::Output {
self.rational.div(rhs.rational).into()
}
}
impl PartialOrd<Self> for FrameRate {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
impl Sub for FrameRate {
type Output = FrameRate;
fn sub(self, rhs: Self) -> Self::Output {
self.rational.sub(rhs.rational).into()
}
}
impl Ord for FrameRate {
fn cmp(&self, other: &Self) -> Ordering {
if self.numerator == 0 && other.denominator == 0 {
return Ordering::Equal;
impl Rem for FrameRate {
type Output = FrameRate;
fn rem(self, rhs: Self) -> Self::Output {
self.rational.rem(rhs.rational).into()
}
}
impl SimpleRangeItem for FrameRate {
const ZERO: Self = FrameRate::frame_rate(0);
}
impl From<Rational32> for FrameRate {
fn from(value: Rational32) -> Self {
FrameRate {
rational: value,
}
if self.denominator == other.denominator {
return self.numerator.cmp(&other.numerator);
}
(self.numerator * other.denominator).cmp(&(other.numerator * self.denominator))
}
}
/// This is a convenience struct that holds all information about the format of a webcam stream.
/// It consists of a [`Resolution`], [`FrameFormat`], and a [`FrameRate`].
#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd)]
@@ -265,7 +310,7 @@ pub struct CameraFormat {
impl CameraFormat {
/// Construct a new [`CameraFormat`]
#[must_use]
pub fn new(resolution: Resolution, format: FrameFormat, frame_rate: FrameRate) -> Self {
pub const fn new(resolution: Resolution, format: FrameFormat, frame_rate: FrameRate) -> Self {
CameraFormat {
resolution,
format,
@@ -275,7 +320,7 @@ impl CameraFormat {
/// [`CameraFormat::new()`], but raw.
#[must_use]
pub fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: FrameRate) -> Self {
pub const fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: FrameRate) -> Self {
CameraFormat {
resolution: Resolution {
width_x: res_x,
@@ -530,374 +575,3 @@ impl Display for ApiBackend {
write!(f, "{self:?}")
}
}
// /// A webcam index that supports both strings and integers. Most backends take an int, but `IPCamera`s take a URL (string).
// #[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
// pub enum CameraIndex {
// Index(u32),
// String(String),
// }
// impl CameraIndex {
// /// Gets the device info's index as an `u32`.
// /// # Errors
// /// If the index is not parsable as a `u32`, this will error.
// pub fn as_index(&self) -> Result<u32, NokhwaError> {
// match self {
// CameraIndex::Index(i) => Ok(*i),
// CameraIndex::String(s) => match s.parse::<u32>() {
// Ok(p) => Ok(p),
// Err(why) => Err(NokhwaError::GetPropertyError {
// property: "index-int".to_string(),
// error: why.to_string(),
// }),
// },
// }
// }
// }
// impl Display for CameraIndex {
// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// match self {
// CameraIndex::Index(idx) => {
// write!(f, "{}", idx)
// }
// CameraIndex::String(ip) => {
// write!(f, "{}", ip)
// }
// }
// }
// }
// impl From<u32> for CameraIndex {
// fn from(v: u32) -> Self {
// CameraIndex::Index(v)
// }
// }
// /// Trait for strings that can be converted to [`CameraIndex`]es.
// pub trait ValidString: AsRef<str> {}
//
// impl ValidString for String {}
// impl<'a> ValidString for &'a String {}
// impl<'a> ValidString for &'a mut String {}
// impl<'a> ValidString for Cow<'a, str> {}
// impl<'a> ValidString for &'a Cow<'a, str> {}
// impl<'a> ValidString for &'a mut Cow<'a, str> {}
// impl<'a> ValidString for &'a str {}
// impl<'a> ValidString for &'a mut str {}
// impl<T> From<T> for CameraIndex
// where
// T: ValidString,
// {
// fn from(v: T) -> Self {
// CameraIndex::String(v.as_ref().to_string())
// }
// }
#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))]
#[inline]
fn decompress<'a>(
data: &'a [u8],
rgba: bool,
) -> Result<mozjpeg::decompress::DecompressStarted<'a>, NokhwaError> {
use mozjpeg::Decompress;
match Decompress::new_mem(data) {
Ok(decompress) => {
let decompressor_res = if rgba {
decompress.rgba()
} else {
decompress.rgb()
};
match decompressor_res {
Ok(decompressor) => Ok(decompressor),
Err(why) => {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: why.to_string(),
})
}
}
}
Err(why) => {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: why.to_string(),
})
}
}
}
/// Converts a MJpeg stream of `&[u8]` into a `Vec<u8>` of RGB888. (R,G,B,R,G,B,...)
/// # Errors
/// If `mozjpeg` fails to read scanlines or setup the decompressor, this will error.
/// # Safety
/// This function uses `unsafe`. The caller must ensure that:
/// - The input data is of the right size, does not exceed bounds, and/or the final size matches with the initial size.
#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))]
#[inline]
pub fn mjpeg_to_rgb(data: &[u8], rgba: bool) -> Result<Vec<u8>, NokhwaError> {
let mut jpeg_decompress = decompress(data, rgba)?;
let scanlines_res: Option<Vec<u8>> = jpeg_decompress.read_scanlines_flat();
// assert!(jpeg_decompress.finish_decompress());
if !jpeg_decompress.finish_decompress() {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: "JPEG Decompressor did not finish.".to_string(),
});
}
match scanlines_res {
Some(pixels) => Ok(pixels),
None => Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: "Failed to get read readlines into RGB888 pixels!".to_string(),
}),
}
}
/// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer.
/// # Errors
/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error.
#[cfg(not(all(feature = "conversions", not(target_arch = "wasm32"))))]
pub fn mjpeg_to_rgb(_data: &[u8], _rgba: bool) -> Result<Vec<u8>, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not available on WASM".to_string(),
))
}
/// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer.
/// # Errors
/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error.
#[cfg(not(all(feature = "conversions", not(target_arch = "wasm32"))))]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))]
#[inline]
pub fn buf_mjpeg_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> {
let mut jpeg_decompress = mozjpeg::decompress(data, rgba)?;
// assert_eq!(dest.len(), jpeg_decompress.min_flat_buffer_size());
if dest.len() != jpeg_decompress.min_flat_buffer_size() {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: "Bad decoded buffer size".to_string(),
});
}
jpeg_decompress.read_scanlines_flat_into(dest);
// assert!(jpeg_decompress.finish_decompress());
if !jpeg_decompress.finish_decompress() {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::MJpeg,
destination: "RGB888".to_string(),
error: "JPEG Decompressor did not finish.".to_string(),
});
}
Ok(())
}
// TODO: deprecate?
/// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer.
/// # Errors
/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error.
#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))]
pub fn buf_mjpeg_to_rgb(_data: &[u8], _dest: &mut [u8], _rgba: bool) -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not available on WASM".to_string(),
))
}
/// Returns the predicted size of the destination Yuv422422 buffer.
#[inline]
pub fn yuyv422_predicted_size(size: usize, rgba: bool) -> usize {
let pixel_size = if rgba { 4 } else { 3 };
// yuyv yields 2 3-byte pixels per yuyv chunk
(size / 4) * (2 * pixel_size)
}
#[inline]
pub fn yuyv422_to_rgb(data: &[u8], rgba: bool) -> Result<Vec<u8>, NokhwaError> {
let capacity = yuyv422_predicted_size(data.len(), rgba);
let mut rgb = vec![0; capacity];
buf_yuyv422_to_rgb(data, &mut rgb, rgba)?;
Ok(rgb)
}
/// Same as [`yuyv422_to_rgb`] but with a destination buffer instead of a return `Vec<u8>`
/// # Errors
/// If the stream is invalid Yuv422, or the destination buffer is not large enough, this will error.
#[inline]
pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> {
let mut buf: Vec<u8> = Vec::new();
if data.len() % 4 != 0 {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::Yuy2_422,
destination: "RGB888".to_string(),
error: "Assertion failure, the YUV stream isn't 4:2:2! (wrong number of bytes)"
.to_string(),
});
}
for chunk in data.chunks_exact(4) {
let y0 = f32::from(chunk[0]);
let u = f32::from(chunk[1]);
let y1 = f32::from(chunk[2]);
let v = f32::from(chunk[3]);
let r0 = y0 + 1.370_705 * (v - 128.);
let g0 = y0 - 0.698_001 * (v - 128.) - 0.337_633 * (u - 128.);
let b0 = y0 + 1.732_446 * (u - 128.);
let r1 = y1 + 1.370_705 * (v - 128.);
let g1 = y1 - 0.698_001 * (v - 128.) - 0.337_633 * (u - 128.);
let b1 = y1 + 1.732_446 * (u - 128.);
if rgba {
buf.extend_from_slice(&[
r0 as u8, g0 as u8, b0 as u8, 255, r1 as u8, g1 as u8, b1 as u8, 255,
]);
} else {
buf.extend_from_slice(&[r0 as u8, g0 as u8, b0 as u8, r1 as u8, g1 as u8, b1 as u8]);
}
}
dest.copy_from_slice(&buf);
Ok(())
}
// equation from https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB
/// Convert `YCbCr` 4:4:4 to a RGB888. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
#[allow(clippy::many_single_char_names)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[must_use]
#[inline]
pub fn yuyv444_to_rgb(y: i32, u: i32, v: i32) -> [u8; 3] {
let c298 = (y - 16) * 298;
let d = u - 128;
let e = v - 128;
let r = ((c298 + 409 * e + 128) >> 8).clamp(0, 255) as u8;
let g = ((c298 - 100 * d - 208 * e + 128) >> 8).clamp(0, 255) as u8;
let b = ((c298 + 516 * d + 128) >> 8).clamp(0, 255) as u8;
[r, g, b]
}
// equation from https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB
/// Convert `YCbCr` 4:4:4 to a RGBA8888. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
///
/// Equivalent to [`yuyv444_to_rgb`] but with an alpha channel attached.
#[allow(clippy::many_single_char_names)]
#[must_use]
#[inline]
pub fn yuyv444_to_rgba(y: i32, u: i32, v: i32) -> [u8; 4] {
let [r, g, b] = yuyv444_to_rgb(y, u, v);
[r, g, b, 255]
}
/// Converts a Yuv422 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
/// # Errors
/// This may error when the data stream size is wrong.
#[inline]
pub fn nv12_to_rgb(
resolution: Resolution,
data: &[u8],
rgba: bool,
) -> Result<Vec<u8>, NokhwaError> {
let pxsize = if rgba { 4 } else { 3 };
let mut dest = vec![0; (pxsize * resolution.width() * resolution.height()) as usize];
buf_nv12_to_rgb(resolution, data, &mut dest, rgba)?;
Ok(dest)
}
// this depresses me
// like, everytime i open this codebase all the life is sucked out of me
// i hate it
/// Converts a Yuv422 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream and outputs it into a destination buffer. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
/// # Errors
/// This may error when the data stream size is wrong.
#[allow(clippy::similar_names)]
#[inline]
pub fn buf_nv12_to_rgb(
resolution: Resolution,
data: &[u8],
out: &mut [u8],
rgba: bool,
) -> Result<(), NokhwaError> {
if resolution.width() % 2 != 0 || resolution.height() % 2 != 0 {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::Nv12,
destination: "RGB".to_string(),
error: "bad resolution".to_string(),
});
}
if data.len() != ((resolution.width() * resolution.height() * 3) / 2) as usize {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::Nv12,
destination: "RGB".to_string(),
error: "bad input buffer size".to_string(),
});
}
let pxsize = if rgba { 4 } else { 3 };
if out.len() != (pxsize * resolution.width() * resolution.height()) as usize {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::Nv12,
destination: "RGB".to_string(),
error: "bad output buffer size".to_string(),
});
}
let rgba_size = if rgba { 4 } else { 3 };
let y_section = (resolution.width() * resolution.height()) as usize;
let width_usize = resolution.width() as usize;
// let height_usize = resolution.height() as usize;
for (hidx, horizontal_row) in data[0..y_section].chunks_exact(width_usize).enumerate() {
for (cidx, column) in horizontal_row.chunks_exact(2).enumerate() {
let u = data[(y_section) + ((hidx / 2) * width_usize) + (cidx * 2)];
let v = data[(y_section) + ((hidx / 2) * width_usize) + (cidx * 2) + 1];
let y0 = column[0];
let y1 = column[1];
let base_index = (hidx * width_usize * rgba_size) + cidx * rgba_size * 2;
if rgba {
let px0 = yuyv444_to_rgba(y0 as i32, u as i32, v as i32);
let px1 = yuyv444_to_rgba(y1 as i32, u as i32, v as i32);
out[base_index] = px0[0];
out[base_index + 1] = px0[1];
out[base_index + 2] = px0[2];
out[base_index + 3] = px0[3];
out[base_index + 4] = px1[0];
out[base_index + 5] = px1[1];
out[base_index + 6] = px1[2];
out[base_index + 7] = px1[3];
} else {
let px0 = yuyv444_to_rgb(y0 as i32, u as i32, v as i32);
let px1 = yuyv444_to_rgb(y1 as i32, u as i32, v as i32);
out[base_index] = px0[0];
out[base_index + 1] = px0[1];
out[base_index + 2] = px0[2];
out[base_index + 3] = px1[0];
out[base_index + 4] = px1[1];
out[base_index + 5] = px1[2];
}
}
}
Ok(())
}
+13 -2
View File
@@ -1,6 +1,10 @@
use core::ops::AddAssign;
pub fn min_max_range<N: Copy + PartialOrd + AddAssign<N> + Sized>(min: N, max: N, step: N) -> Vec<N> {
pub fn min_max_range<N: Copy + PartialOrd + AddAssign<N> + Sized>(
min: N,
max: N,
step: N,
) -> Vec<N> {
let mut counter = min;
let mut nums = vec![min];
@@ -8,7 +12,7 @@ pub fn min_max_range<N: Copy + PartialOrd + AddAssign<N> + Sized>(min: N, max: N
counter += step;
if counter > max {
break
break;
}
nums.push(counter);
@@ -16,3 +20,10 @@ pub fn min_max_range<N: Copy + PartialOrd + AddAssign<N> + Sized>(min: N, max: N
nums
}
pub trait Distance<T>
where
T: PartialEq,
{
fn distance_from(&self, other: &Self) -> T;
}
+4 -4
View File
@@ -21,7 +21,7 @@ use nokhwa_bindings_macos::{
AVCaptureVideoDataOutput,
};
use nokhwa_core::{
buffer::Buffer,
frame_buffer::FrameBuffer,
error::NokhwaError,
pixel_format::RgbFormat,
traits::CaptureTrait,
@@ -278,11 +278,11 @@ impl CaptureTrait for AVFoundationCaptureDevice {
}
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
self.refresh_camera_format()?;
let cfmt = self.camera_format();
let b = self.frame_raw()?;
let buffer = Buffer::new(cfmt.resolution(), b.as_ref(), cfmt.format());
let buffer = FrameBuffer::new(cfmt.resolution(), b.as_ref(), cfmt.format());
let _ = self.frame_buffer_receiver.drain();
Ok(buffer)
}
@@ -476,7 +476,7 @@ impl CaptureTrait for AVFoundationCaptureDevice {
todo!()
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
todo!()
}
+3 -3
View File
@@ -6,7 +6,7 @@ use nokhwa_core::format_request::FormatRequest;
use serde::{de, Serialize};
use wasm_bindgen_futures::JsFuture;
use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator};
use nokhwa_core::buffer::Buffer;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_format::FrameFormat;
@@ -373,7 +373,7 @@ impl CaptureTrait for BrowserCaptureDevice {
todo!()
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
todo!()
}
@@ -422,7 +422,7 @@ impl AsyncCaptureTrait for BrowserCaptureDevice {
todo!()
}
async fn frame_async(&mut self) -> Result<Buffer, NokhwaError> {
async fn frame_async(&mut self) -> Result<FrameBuffer, NokhwaError> {
todo!()
}
+3 -3
View File
@@ -15,7 +15,7 @@
*/
use nokhwa_bindings_windows::wmf::MediaFoundationDevice;
use nokhwa_core::{
buffer::Buffer,
frame_buffer::FrameBuffer,
error::NokhwaError,
pixel_format::RgbFormat,
traits::CaptureTrait,
@@ -242,10 +242,10 @@ impl CaptureTrait for MediaFoundationCaptureDevice {
self.inner.is_stream_open()
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
self.refresh_camera_format()?;
let self_ctrl = self.camera_format();
Ok(Buffer::new(
Ok(FrameBuffer::new(
self_ctrl.resolution(),
&self.inner.raw_bytes()?,
self_ctrl.format(),
+3 -3
View File
@@ -16,7 +16,7 @@
use nokhwa_core::types::RequestedFormatType;
use nokhwa_core::{
buffer::Buffer,
frame_buffer::FrameBuffer,
error::NokhwaError,
traits::CaptureTrait,
types::{
@@ -525,7 +525,7 @@ impl CaptureTrait for OpenCvCaptureDevice {
self.video_capture.is_opened().unwrap_or(false)
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
let camera_resolution = self.camera_format.resolution();
let image_data = {
let mut data = self.frame_raw()?.to_vec();
@@ -535,7 +535,7 @@ impl CaptureTrait for OpenCvCaptureDevice {
);
data
};
Ok(Buffer::new(
Ok(FrameBuffer::new(
camera_resolution,
&image_data,
self.camera_format.format(),
+1 -1
View File
@@ -13,7 +13,7 @@ use nokhwa_bindings_linux::{
}
};
use nokhwa_core::{
capture::{Open, Setting},
camera::{Open, Setting},
error::{NokhwaError, NokhwaResult},
frame_format::FrameFormat,
properties::CameraProperties,
+2 -2
View File
@@ -18,7 +18,7 @@ use nokhwa_core::format_request::FormatFilter;
use nokhwa_core::frame_format::SourceFrameFormat;
use nokhwa_core::traits::Backend;
use nokhwa_core::{
buffer::Buffer,
frame_buffer::FrameBuffer,
error::NokhwaError,
pixel_format::FormatDecoder,
traits::CaptureTrait,
@@ -136,7 +136,7 @@ impl CaptureTrait for Camera {
todo!()
}
fn frame(&mut self) -> Result<Buffer, NokhwaError> {
fn frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
todo!()
}
+2 -2
View File
@@ -48,7 +48,7 @@ pub mod threaded;
pub use camera::Camera;
pub use init::*;
pub use nokhwa_core::buffer::Buffer;
pub use nokhwa_core::frame_buffer::FrameBuffer;
pub use nokhwa_core::error::NokhwaError;
pub use query::*;
#[cfg(feature = "output-threaded")]
@@ -68,5 +68,5 @@ pub mod camera_traits {
}
pub mod buffer {
pub use nokhwa_core::buffer::*;
pub use nokhwa_core::frame_buffer::*;
}
+15 -15
View File
@@ -16,7 +16,7 @@
use crate::Camera;
use nokhwa_core::{
buffer::Buffer,
frame_buffer::FrameBuffer,
error::NokhwaError,
types::{
ApiBackend, CameraFormat, CameraIndex, CameraInfo,
@@ -36,11 +36,11 @@ use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraCont
type AtomicLock<T> = Arc<Mutex<T>>;
pub type CallbackFn = fn(
_camera: &Arc<Mutex<Camera>>,
_frame_callback: &Arc<Mutex<Option<Box<dyn FnMut(Buffer) + Send + 'static>>>>,
_last_frame_captured: &Arc<Mutex<Buffer>>,
_frame_callback: &Arc<Mutex<Option<Box<dyn FnMut(FrameBuffer) + Send + 'static>>>>,
_last_frame_captured: &Arc<Mutex<FrameBuffer>>,
_die_bool: &Arc<AtomicBool>,
);
type HeldCallbackType = Arc<Mutex<Box<dyn FnMut(Buffer) + Send + 'static>>>;
type HeldCallbackType = Arc<Mutex<Box<dyn FnMut(FrameBuffer) + Send + 'static>>>;
/// Creates a camera that runs in a different thread that you can use a callback to access the frames of.
/// It uses a `Arc` and a `Mutex` to ensure that this feels like a normal camera, but callback based.
@@ -58,7 +58,7 @@ type HeldCallbackType = Arc<Mutex<Box<dyn FnMut(Buffer) + Send + 'static>>>;
pub struct CallbackCamera {
camera: AtomicLock<Camera>,
frame_callback: HeldCallbackType,
last_frame_captured: AtomicLock<Buffer>,
last_frame_captured: AtomicLock<FrameBuffer>,
die_bool: Arc<AtomicBool>,
current_camera: CameraInfo,
handle: AtomicLock<Option<JoinHandle<()>>>,
@@ -72,7 +72,7 @@ impl CallbackCamera {
pub fn new(
index: CameraIndex,
format: RequestedFormat,
callback: impl FnMut(Buffer) + Send + 'static,
callback: impl FnMut(FrameBuffer) + Send + 'static,
) -> Result<Self, NokhwaError> {
let arc_camera = Arc::new(Mutex::new(Camera::new(index, format)?));
let current_camera = arc_camera
@@ -86,7 +86,7 @@ impl CallbackCamera {
Ok(CallbackCamera {
camera: arc_camera,
frame_callback: Arc::new(Mutex::new(Box::new(callback))),
last_frame_captured: Arc::new(Mutex::new(Buffer::new(
last_frame_captured: Arc::new(Mutex::new(FrameBuffer::new(
Resolution::new(0, 0),
&vec![],
FrameFormat::GRAY,
@@ -100,12 +100,12 @@ impl CallbackCamera {
/// Allows creation of a [`Camera`] with a custom backend. This is useful if you are creating e.g. a custom module.
///
/// You **must** have set a format beforehand.
pub fn with_custom(camera: Camera, callback: impl FnMut(Buffer) + Send + 'static) -> Self {
pub fn with_custom(camera: Camera, callback: impl FnMut(FrameBuffer) + Send + 'static) -> Self {
let current_camera = camera.info().clone();
CallbackCamera {
camera: Arc::new(Mutex::new(camera)),
frame_callback: Arc::new(Mutex::new(Box::new(callback))),
last_frame_captured: Arc::new(Mutex::new(Buffer::new(
last_frame_captured: Arc::new(Mutex::new(FrameBuffer::new(
Resolution::new(0, 0),
&vec![],
FrameFormat::GRAY,
@@ -183,7 +183,7 @@ impl CallbackCamera {
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = Buffer::new(
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = FrameBuffer::new(
new_fmt.resolution(),
&Vec::default(),
self.camera_format()?.format(),
@@ -266,7 +266,7 @@ impl CallbackCamera {
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? =
Buffer::new(new_res, &Vec::default(), self.camera_format()?.format());
FrameBuffer::new(new_res, &Vec::default(), self.camera_format()?.format());
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
@@ -479,7 +479,7 @@ impl CallbackCamera {
/// Sets the frame callback to the new specified function. This function will be called instead of the previous one(s).
pub fn set_callback(
&mut self,
callback: impl FnMut(Buffer) + Send + 'static,
callback: impl FnMut(FrameBuffer) + Send + 'static,
) -> Result<(), NokhwaError> {
*self
.frame_callback
@@ -494,7 +494,7 @@ impl CallbackCamera {
/// Polls the camera for a frame, analogous to [`Camera::frame`](crate::Camera::frame)
/// # Errors
/// This will error if the camera fails to capture a frame.
pub fn poll_frame(&mut self) -> Result<Buffer, NokhwaError> {
pub fn poll_frame(&mut self) -> Result<FrameBuffer, NokhwaError> {
let frame = self
.camera
.lock()
@@ -508,7 +508,7 @@ impl CallbackCamera {
}
/// Gets the last frame captured by the camera.
pub fn last_frame(&self) -> Result<Buffer, NokhwaError> {
pub fn last_frame(&self) -> Result<FrameBuffer, NokhwaError> {
Ok(self
.last_frame_captured
.lock()
@@ -549,7 +549,7 @@ impl Drop for CallbackCamera {
fn camera_frame_thread_loop(
camera: AtomicLock<Camera>,
frame_callback: HeldCallbackType,
last_frame_captured: AtomicLock<Buffer>,
last_frame_captured: AtomicLock<FrameBuffer>,
die_bool: Arc<AtomicBool>,
) {
loop {