From 33f078752b08edad78fe3384eb453956eb9dddc3 Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Thu, 16 Jan 2025 19:17:58 +0900 Subject: [PATCH] in progress v4l2 implementation, mostly stable core --- Cargo.lock | 684 +++++++++++-------------- Cargo.toml | 15 +- flake.lock | 18 +- flake.nix | 87 ++-- nokhwa-bindings-linux/Cargo.toml | 17 +- nokhwa-bindings-linux/src/v4l2.rs | 504 ++++++++++++++---- nokhwa-bindings-macos/src/lib.rs | 2 +- nokhwa-bindings-windows/src/lib.rs | 4 +- nokhwa-core/Cargo.toml | 5 +- nokhwa-core/src/camera.rs | 10 +- nokhwa-core/src/control.rs | 425 +++++++++++++++ nokhwa-core/src/error.rs | 2 - nokhwa-core/src/frame_format.rs | 6 +- nokhwa-core/src/lib.rs | 2 +- nokhwa-core/src/platform.rs | 9 + nokhwa-core/src/properties.rs | 446 ---------------- nokhwa-core/src/ranges.rs | 336 ++---------- nokhwa-core/src/stream.rs | 17 +- nokhwa-core/src/types.rs | 9 +- src/backends/capture/avfoundation.rs | 2 +- src/backends/capture/browser_camera.rs | 2 +- src/backends/capture/msmf_backend.rs | 2 +- src/backends/capture/opencv_backend.rs | 2 +- src/backends/capture/v4l2_backend.rs | 2 +- src/camera.rs | 2 +- src/threaded.rs | 4 +- 26 files changed, 1286 insertions(+), 1328 deletions(-) create mode 100644 nokhwa-core/src/control.rs delete mode 100644 nokhwa-core/src/properties.rs diff --git a/Cargo.lock b/Cargo.lock index 7b93601..5af5536 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,16 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" +dependencies = [ + "unicode-width", + "yansi-term", +] + [[package]] name = "anstream" version = "0.6.13" @@ -117,6 +127,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + [[package]] name = "approx" version = "0.5.1" @@ -232,6 +248,27 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "annotate-snippets", + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.87", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -403,6 +440,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -470,7 +517,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", ] [[package]] @@ -611,34 +658,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "com" +name = "convert_case" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "com_macros", + "unicode-segmentation", ] [[package]] -name = "com_macros" -version = "0.6.0" +name = "cookie-factory" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" dependencies = [ - "com_macros_support", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "com_macros_support" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "futures", ] [[package]] @@ -962,25 +996,24 @@ dependencies = [ "winapi", ] -[[package]] -name = "d3d12" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" -dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.3", - "winapi", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -997,25 +1030,40 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.87", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", "quote", "syn 1.0.109", ] [[package]] -name = "dcv-color-primitives" -version = "0.6.1" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ad62edfed069700a5b33af6babd29c498d7e33eb01d96ffa8841ee1841634c" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "paste", - "wasm-bindgen", + "darling_core 0.20.10", + "quote", + "syn 2.0.87", ] [[package]] @@ -1026,6 +1074,37 @@ dependencies = [ "nokhwa-core", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.87", +] + [[package]] name = "directories" version = "4.0.1" @@ -1441,7 +1520,7 @@ dependencies = [ "serde", "skeptic", "smart-default", - "toml", + "toml 0.5.11", "typed-arena", "wgpu 0.14.2", "winit", @@ -1502,18 +1581,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "glow" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "glow" version = "0.14.2" @@ -1526,15 +1593,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "glutin_wgl_sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" -dependencies = [ - "gl_generator", -] - [[package]] name = "glutin_wgl_sys" version = "0.6.0" @@ -1552,7 +1610,7 @@ checksum = "a3676f482c536a985fca36ce320a5e5b8fafd7b260806742af1963b71c5dc38c" dependencies = [ "glyph_brush_draw_cache", "glyph_brush_layout", - "ordered-float 4.2.0", + "ordered-float 4.6.0", "rustc-hash", "twox-hash", ] @@ -1620,19 +1678,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "gpu-allocator" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" -dependencies = [ - "log", - "presser", - "thiserror 1.0.69", - "winapi", - "windows 0.52.0", -] - [[package]] name = "gpu-allocator" version = "0.27.0" @@ -1711,21 +1756,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "hassle-rs" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" -dependencies = [ - "bitflags 2.6.0", - "com", - "libc", - "libloading 0.8.3", - "thiserror 1.0.69", - "widestring", - "winapi", -] - [[package]] name = "heck" version = "0.5.0" @@ -1856,6 +1886,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1999,6 +2038,34 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libspa" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" +dependencies = [ + "bitflags 2.6.0", + "cc", + "convert_case", + "cookie-factory", + "libc", + "libspa-sys", + "nix 0.27.1", + "nom", + "system-deps", +] + +[[package]] +name = "libspa-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f" +dependencies = [ + "bindgen 0.69.5", + "cc", + "system-deps", +] + [[package]] name = "libudev-sys" version = "0.1.4" @@ -2184,21 +2251,6 @@ dependencies = [ "objc", ] -[[package]] -name = "metal" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" -dependencies = [ - "bitflags 2.6.0", - "block", - "core-graphics-types", - "foreign-types 0.5.0", - "log", - "objc", - "paste", -] - [[package]] name = "metal" version = "0.29.0" @@ -2292,27 +2344,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "naga" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" -dependencies = [ - "arrayvec 0.7.4", - "bit-set 0.5.3", - "bitflags 2.6.0", - "codespan-reporting", - "hexf-parse", - "indexmap 2.2.6", - "log", - "num-traits", - "rustc-hash", - "spirv 0.3.0+sdk-1.3.268.0", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", -] - [[package]] name = "naga" version = "23.0.0" @@ -2394,7 +2425,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro-crate", "proc-macro2", "quote", @@ -2444,12 +2475,22 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "nokhwa" version = "0.11.0" dependencies = [ "async-trait", - "dcv-color-primitives", "flume 0.11.0", "image 0.25.0", "js-sys", @@ -2458,26 +2499,25 @@ dependencies = [ "nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-core", - "opencv 0.92.0", + "opencv", "paste", - "regex", "rgb", "serde", "serde-wasm-bindgen", - "thiserror 1.0.69", "usb_enumeration", "wasm-bindgen", "wasm-bindgen-futures", "wasm-rs-async-executor", "web-sys", - "wgpu 0.20.0", + "wgpu 23.0.1", ] [[package]] name = "nokhwa-bindings-linux" -version = "0.1.0" +version = "0.2.0" dependencies = [ "nokhwa-core", + "pipewire", "v4l", "v4l2-sys-mit", ] @@ -2512,13 +2552,14 @@ version = "0.2.0" dependencies = [ "async-trait", "bytes", + "derive_builder", "flume 0.11.0", "futures", "image 0.25.0", "num-rational 0.4.2", "num-traits", - "opencv 0.93.1", - "rgb", + "opencv", + "ordered-float 4.6.0", "serde", "thiserror 2.0.0", "wgpu 23.0.1", @@ -2699,27 +2740,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "opencv" -version = "0.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e19ccfa4237826b2213bda6dd30875b9ed184c5318ec64d3c269132d128212c" -dependencies = [ - "cc", - "dunce", - "jobserver", - "libc", - "num-traits", - "once_cell", - "opencv-binding-generator 0.90.0", - "pkg-config", - "rgb", - "semver", - "shlex", - "vcpkg", - "windows 0.56.0", -] - [[package]] name = "opencv" version = "0.93.1" @@ -2732,28 +2752,15 @@ dependencies = [ "libc", "num-traits", "once_cell", - "opencv-binding-generator 0.91.0", + "opencv-binding-generator", "pkg-config", + "rgb", "semver", "shlex", "vcpkg", "windows 0.58.0", ] -[[package]] -name = "opencv-binding-generator" -version = "0.90.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ddd32f38d5cd32c79a38da363528ced3149a48f1e686b7a12913a184a84cd5" -dependencies = [ - "clang", - "clang-sys", - "dunce", - "once_cell", - "percent-encoding", - "regex", -] - [[package]] name = "opencv-binding-generator" version = "0.91.0" @@ -2780,9 +2787,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.2.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ "num-traits", ] @@ -2875,6 +2882,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pipewire" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" +dependencies = [ + "anyhow", + "bitflags 2.6.0", + "libc", + "libspa", + "libspa-sys", + "nix 0.27.1", + "once_cell", + "pipewire-sys", + "thiserror 1.0.69", +] + +[[package]] +name = "pipewire-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112" +dependencies = [ + "bindgen 0.69.5", + "libspa-sys", + "system-deps", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -2929,7 +2964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -3247,6 +3282,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "servo-fontconfig" version = "0.5.1" @@ -3408,9 +3452,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -3434,6 +3478,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.12", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.10.1" @@ -3558,11 +3621,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.12", +] + [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3572,7 +3650,20 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.2.6", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.22", ] [[package]] @@ -3664,6 +3755,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.11" @@ -3713,7 +3810,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" dependencies = [ - "bindgen", + "bindgen 0.65.1", ] [[package]] @@ -3734,6 +3831,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" @@ -3943,32 +4046,6 @@ dependencies = [ "wgpu-types 0.14.1", ] -[[package]] -name = "wgpu" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ff1bfee408e1028e2e3acbf6d32d98b08a5a059ccbf5f33305534453ba5d3e" -dependencies = [ - "arrayvec 0.7.4", - "cfg-if 1.0.0", - "cfg_aliases", - "document-features", - "js-sys", - "log", - "naga 0.20.0", - "parking_lot", - "profiling", - "raw-window-handle 0.6.0", - "smallvec", - "static_assertions", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wgpu-core 0.20.0", - "wgpu-hal 0.20.0", - "wgpu-types 0.20.0", -] - [[package]] name = "wgpu" version = "23.0.1" @@ -4018,33 +4095,6 @@ dependencies = [ "wgpu-types 0.14.1", ] -[[package]] -name = "wgpu-core" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6a86eaa5e763e59c73cf9e97d55fffd4dfda69fd8bda19589fcf851ddfef1f" -dependencies = [ - "arrayvec 0.7.4", - "bit-vec 0.6.3", - "bitflags 2.6.0", - "cfg_aliases", - "codespan-reporting", - "document-features", - "indexmap 2.2.6", - "log", - "naga 0.20.0", - "once_cell", - "parking_lot", - "profiling", - "raw-window-handle 0.6.0", - "rustc-hash", - "smallvec", - "thiserror 1.0.69", - "web-sys", - "wgpu-hal 0.20.0", - "wgpu-types 0.20.0", -] - [[package]] name = "wgpu-core" version = "23.0.1" @@ -4083,7 +4133,7 @@ dependencies = [ "bitflags 1.3.2", "block", "core-graphics-types", - "d3d12 0.5.0", + "d3d12", "foreign-types 0.3.2", "fxhash", "glow 0.11.2", @@ -4109,51 +4159,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wgpu-hal" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d71c8ae05170583049b65ee562fd839fdc0b3e9ddb84f4e40c9d5f8ea0d4c8c" -dependencies = [ - "android_system_properties", - "arrayvec 0.7.4", - "ash 0.37.3+1.3.251", - "bit-set 0.5.3", - "bitflags 2.6.0", - "block", - "cfg_aliases", - "core-graphics-types", - "d3d12 0.20.0", - "glow 0.13.1", - "glutin_wgl_sys 0.5.0", - "gpu-alloc 0.6.0", - "gpu-allocator 0.25.0", - "gpu-descriptor 0.3.0", - "hassle-rs", - "js-sys", - "khronos-egl 6.0.0", - "libc", - "libloading 0.8.3", - "log", - "metal 0.28.0", - "naga 0.20.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc", - "once_cell", - "parking_lot", - "profiling", - "range-alloc", - "raw-window-handle 0.6.0", - "renderdoc-sys 1.1.0", - "rustc-hash", - "smallvec", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", - "wgpu-types 0.20.0", - "winapi", -] - [[package]] name = "wgpu-hal" version = "23.0.1" @@ -4170,9 +4175,9 @@ dependencies = [ "cfg_aliases", "core-graphics-types", "glow 0.14.2", - "glutin_wgl_sys 0.6.0", + "glutin_wgl_sys", "gpu-alloc 0.6.0", - "gpu-allocator 0.27.0", + "gpu-allocator", "gpu-descriptor 0.3.0", "js-sys", "khronos-egl 6.0.0", @@ -4196,7 +4201,7 @@ dependencies = [ "web-sys", "wgpu-types 23.0.0", "windows 0.58.0", - "windows-core 0.58.0", + "windows-core", ] [[package]] @@ -4208,17 +4213,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "wgpu-types" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" -dependencies = [ - "bitflags 2.6.0", - "js-sys", - "web-sys", -] - [[package]] name = "wgpu-types" version = "23.0.0" @@ -4242,12 +4236,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "widestring" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" - [[package]] name = "winapi" version = "0.3.9" @@ -4294,54 +4282,13 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.56.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" -dependencies = [ - "windows-core 0.56.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.56.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" -dependencies = [ - "windows-implement 0.56.0", - "windows-interface 0.56.0", - "windows-result 0.1.1", + "windows-core", "windows-targets 0.52.6", ] @@ -4351,24 +4298,13 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", + "windows-implement", + "windows-interface", + "windows-result", "windows-strings", "windows-targets 0.52.6", ] -[[package]] -name = "windows-implement" -version = "0.56.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "windows-implement" version = "0.58.0" @@ -4380,17 +4316,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "windows-interface" -version = "0.56.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "windows-interface" version = "0.58.0" @@ -4402,15 +4327,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "windows-result" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.2.0" @@ -4426,7 +4342,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-targets 0.52.6", ] @@ -4697,6 +4613,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +dependencies = [ + "memchr", +] + [[package]] name = "wio" version = "0.2.2" @@ -4735,6 +4660,15 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +[[package]] +name = "yansi-term" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" +dependencies = [ + "winapi", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 43b8d65..7ac5df7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,17 +41,12 @@ docs-features = [] test-fail-warning = [] [dependencies] -thiserror = "1.0" paste = "1.0" [dependencies.mozjpeg] version = "0.10" optional = true -[dependencies.dcv-color-primitives] -version = "0.6" -optional = true - [dependencies.nokhwa-core] version = "0.2" path = "nokhwa-core" @@ -73,11 +68,11 @@ version = "0.2" optional = true [dependencies.wgpu] -version = "0.20" +version = "23" optional = true [dependencies.opencv] -version = "0.92" +version = "0.93" default-features = false features = ["videoio"] optional = true @@ -97,14 +92,10 @@ path = "nokhwa-bindings-macos" optional = true [dependencies.nokhwa-bindings-linux] -version = "0.1" +version = "0.2" path = "nokhwa-bindings-linux" optional = true -[dependencies.regex] -version = "1.7" -optional = true - [dependencies.web-sys] version = "0.3" features = [ diff --git a/flake.lock b/flake.lock index 4913fdf..187512c 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733097829, - "narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=", + "lastModified": 1736420959, + "narHash": "sha256-dMGNa5UwdtowEqQac+Dr0d2tFO/60ckVgdhZU9q2E2o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a", + "rev": "32af3611f6f05655ca166a0b1f47b57c762b5192", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1728538411, - "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "lastModified": 1736320768, + "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1733193245, - "narHash": "sha256-nwvKoPi3S6XyliqBRuC+01QFF0k94ZOvnoZtbGi/ObM=", + "lastModified": 1736649126, + "narHash": "sha256-XCw5sv/ePsroqiF3lJM6Y2X9EhPdHeE47gr3Q8b0UQw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "3458f7f946ba61d1a1069aedcc17d7b7616f23cd", + "rev": "162ab0edc2936508470199b2e8e6c444a2535019", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 10f4496..c1d6ef4 100644 --- a/flake.nix +++ b/flake.nix @@ -5,50 +5,59 @@ rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - overlays = [ (import rust-overlay) ]; + outputs = { + self, + nixpkgs, + rust-overlay, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { - inherit system overlays; + inherit system; + overlays = [rust-overlay.overlays.default]; }; - in - { + rustbin = pkgs.rust-bin.selectLatestNightlyWith (toolchain: + toolchain.default.override { + extensions = ["rust-src"]; + }); + in { + formatter = pkgs.alejandra; + devShells.default = pkgs.mkShell { - #LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - #BINDGEN_EXTRA_CLANG_ARGS = "-isystem ${pkgs.libclang.lib}/lib/clang/${flake-utils.lib.getVersion pkgs.clang}/include"; + packages = [ + rustbin + ] ++ (with pkgs; [ + llvmPackages.libclang.lib + llvmPackages.clang + pkg-config + cmake + vcpkg + rustPlatform.bindgenHook + xmlstarlet + opencv + alsa-lib + systemdLibs + cmake + fontconfig + linuxHeaders + v4l-utils + libv4l + pipewire + ]); - buildInputs = with pkgs; [ - rust-bin.stable.latest.default - rust-bin.stable.latest.rustfmt - rust-bin.stable.latest.clippy - ]; - nativeBuildInputs = [ - pkgs.pkg-config - pkgs.cmake - pkgs.vcpkg - ]; - packages = with pkgs; [ - rust-analyzer - pkg-config - opencv - alsa-lib - systemdLibs - cmake - fontconfig - linuxHeaders - rustPlatform.bindgenHook - llvmPackages.libclang.lib - llvmPackages.clang - v4l-utils - libv4l - ]; - LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - shellHook = '' - export LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - cargo version + env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library"; + env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + + shellHook = let + pathToRustProject = "/project/component[@name='RustProjectSettings']"; + in + '' + echo "WONDERHOOOOOY!!!!" + xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='explicitPathToStdlib']/@value" --value "${rustbin}/lib/rustlib/src/rust/library" .idea/workspace.xml + xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='toolchainHomeDirectory']/@value" --value "${rustbin}/bin" .idea/workspace.xml ''; - }; } ); diff --git a/nokhwa-bindings-linux/Cargo.toml b/nokhwa-bindings-linux/Cargo.toml index dab1c1b..fe174c3 100644 --- a/nokhwa-bindings-linux/Cargo.toml +++ b/nokhwa-bindings-linux/Cargo.toml @@ -1,22 +1,25 @@ [package] name = "nokhwa-bindings-linux" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/l1npengtul/nokhwa" -description = "The V4L2 bindings crate for `nokhwa`" -keywords = ["v4l", "v4l2", "linux", "capture", "webcam"] +description = "The Linux V4L2 bindings crate for `nokhwa`" +keywords = ["v4l", "v4l2", "linux", "nokhwa", "webcam"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] v4l2 = ["v4l", "v4l2-sys-mit"] +pw = ["pipewire"] [dependencies] +v4l = { version = "0.14", features = ["v4l2"], optional = true } +v4l2-sys-mit = { version = "0.3", optional = true } + +[dependencies.pipewire] +version = "0.8" +optional = true [dependencies.nokhwa-core] version = "0.2" path = "../nokhwa-core" - -[target.'cfg(target_os="linux")'.dependencies] -v4l = { version = "0.14", optional = true } -v4l2-sys-mit = { version = "0.3", optional = true } \ No newline at end of file diff --git a/nokhwa-bindings-linux/src/v4l2.rs b/nokhwa-bindings-linux/src/v4l2.rs index fc6a79a..c9e2f73 100644 --- a/nokhwa-bindings-linux/src/v4l2.rs +++ b/nokhwa-bindings-linux/src/v4l2.rs @@ -1,44 +1,70 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::sync::Arc; -use v4l::{Device, Format, FourCC, Fraction}; -use v4l2_sys_mit::{V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_BACKLIGHT_COMPENSATION, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, V4L2_CID_DO_WHITE_BALANCE, V4L2_CID_EXPOSURE, V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_GAIN, V4L2_CID_GAMMA, V4L2_CID_HUE, V4L2_CID_HUE_AUTO, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_PAN_ABSOLUTE, V4L2_CID_PAN_RELATIVE, V4L2_CID_SATURATION, V4L2_CID_SHARPNESS, V4L2_CID_TILT_ABSOLUTE, V4L2_CID_TILT_RELATIVE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE}; -use v4l::device::Handle; +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::num::NonZeroI32; +use v4l::context::enum_devices; +use v4l::{Capabilities, Device, Format, FourCC, Fraction, FrameInterval}; +use v4l::control::{Description, Flags, MenuItem, Type, Value}; use v4l::frameinterval::FrameIntervalEnum; -use v4l::prelude::MmapStream; -use v4l::video::{Capture as V4lCapture, Output}; +use v4l::video::Output; use v4l::video::output::Parameters; -use nokhwa_core::frame_buffer::FrameBuffer; -use nokhwa_core::camera::{Camera, Open, Setting, Capture}; -use nokhwa_core::properties::{CameraProperties, CameraPropertyFlag, CameraPropertyId, CameraPropertyValue}; -use nokhwa_core::{define_back_and_fourth_control, define_back_and_fourth_frame_format}; +use nokhwa_core::camera::{Camera, Capture, Setting}; use nokhwa_core::error::{NokhwaError, NokhwaResult}; use nokhwa_core::frame_format::FrameFormat; +use nokhwa_core::platform::{Backends, PlatformTrait}; +use nokhwa_core::control::{ControlBody, ControlFlags, ControlId, ControlValue, ControlValueDescriptor, Controls}; +use nokhwa_core::stream::Stream; use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution}; +use v4l2_sys_mit::{V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_CID_FOCUS_AUTO, V4L2_CID_AUTO_FOCUS_STATUS, V4L2_CID_ISO_SENSITIVITY, V4L2_CID_EXPOSURE_AUTO, V4L2_CID_AUTO_EXPOSURE_BIAS, V4L2_CID_EXPOSURE_METERING, V4L2_CID_EXPOSURE_ABSOLUTE, V4L2_CID_ISO_SENSITIVITY_AUTO, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_FLASH_LED_MODE, V4L2_CID_FLASH_STROBE, V4L2_CID_FLASH_STROBE_STATUS, V4L2_CID_CAMERA_ORIENTATION, V4L2_CTRL_FLAG_DISABLED, V4L2_CID_FLASH_STROBE_STOP, v4l2_querymenu}; +use v4l::device::Handle; +use v4l::v4l2::ioctl; +use nokhwa_core::ranges::Range; -const NULL_FCC: &'static [u8; 4] = &[0x00, 0x00, 0x00, 0x00]; +fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> CameraInformation { + let name = capabilities.card; + let description = capabilities.driver; + let misc = format!("{} v{}.{}.{} Flags: {}", capabilities.bus, capabilities.version.0, capabilities.version.1, capabilities.version.2, capabilities.capabilities); -pub use v4l2_sys_mit::*; -pub use v4l::*; - -fn func_u8_8_to_fcc(u8_8: [u8; 8]) -> FrameFormatIntermediate { - FrameFormatIntermediate([u8_8[0], u8_8[1], u8_8[2], u8_8[3]]) + CameraInformation::new(name, description, misc, CameraIndex::Index(index)) } -fn func_fcc_to_u8_8(u8_4: [u8; 4]) -> [u8; 8] { - [u8_4[0], u8_4[1], u8_4[2], u8_4[3], 0x00, 0x00, 0x00, 0x00] + +macro_rules! define_back_and_forth { + ( $($frame_format:expr => $fourcc:expr ,)+ ) => { + fn frame_format_to_fourcc(frame_format: FrameFormat) -> Result { + match frame_format { + $( + $frame_format => Ok(FourCC::new($fourcc)), + )+ + FrameFormat::Custom(def) => { + // if 4-7 is set (non-null) return an error. + if def[4..=7] != [0x00, 0x00, 0x00, 0x00] { + return Err(NokhwaError::ConversionError("Invalid: Custom bytes 4-7 are set (linux only uses 0-3)".to_string())) + } + Ok(FourCC::new(&[def[0], def[1], def[2], def[3]])) + } + _ => { + return Err(NokhwaError::ConversionError("Unsupported FrameFormat".to_string())) + }} + } + + fn fourcc_to_frame_format(four_cc: FourCC) -> FrameFormat { + match &four_cc.repr { + $( + $fourcc => $frame_format + )+ + custom => FrameFormat::Custom([ custom[0], custom[1], custom[2], custom[3], 0x00, 0x00, 0x00, 0x00 ]) + } + } + } } -fn value_to_fcc_type(v: &[u8; 4]) -> [u8;4] { - *v -} -define_back_and_fourth_frame_format!([u8;4], { +define_back_and_forth!( FrameFormat::H265 => b"HEVC", FrameFormat::H264 => b"H264", + FrameFormat::Avc1 => b"AVC1", FrameFormat::H263 => b"H263", FrameFormat::Av1 => b"AV1F", - FrameFormat::Avc1 => b"AVC1", FrameFormat::Mpeg1 => b"MPG1", FrameFormat::Mpeg2 => b"MPG2", FrameFormat::Mpeg4 => b"MPG4", @@ -50,107 +76,365 @@ define_back_and_fourth_frame_format!([u8;4], { FrameFormat::Yuyv422 => b"YUYV", FrameFormat::Uyvy422 => b"UYVY", FrameFormat::Yvyu422 => b"YVYU", + FrameFormat::Yv12 => b"YV12", FrameFormat::Nv12 => b"NV12", FrameFormat::Nv21 => b"NV21", - FrameFormat::Yv12 => b"YV12", FrameFormat::I420 => b"YU12", FrameFormat::Yvu9 => b"YVU9", FrameFormat::Luma8 => b"GREY", FrameFormat::Luma16 => b"Y16 ", FrameFormat::Depth16 => b"Z16 ", - FrameFormat::Rgb332 => b"RGB3", + FrameFormat::Rgb332 => b"RGB1", + FrameFormat::Rgb888 => b"RGB3", + FrameFormat::Bgr888 => b"BGR3", + FrameFormat::BgrA8888 => b"RA24", FrameFormat::RgbA8888 => b"AB24", FrameFormat::ARgb8888 => b"BA24", - FrameFormat::Rgb555 => b"RX15", - FrameFormat::Rgb565 => b"RGBP", FrameFormat::Bayer8 => b"BA81", FrameFormat::Bayer16 => b"BYR2", -}, func_u8_8_to_fcc, func_fcc_to_u8_8, value_to_fcc_type); +); -fn linux_id_to_str(id: u32) -> String { - id.to_string() -} - -fn str_to_linux_id(id: &str) -> Option { - u32::from_str(id).ok() -} - -define_back_and_fourth_control!(u32, { - CameraPropertyId::BacklightCompensation, None => V4L2_CID_BACKLIGHT_COMPENSATION, - CameraPropertyId::Brightness, None => V4L2_CID_BRIGHTNESS, - CameraPropertyId::Contrast, None => V4L2_CID_CONTRAST, - CameraPropertyId::Exposure, None => V4L2_CID_EXPOSURE, - CameraPropertyId::Focus, Some(CameraPropertyFlag::Relative) => V4L2_CID_FOCUS_RELATIVE, - CameraPropertyId::Focus, Some(CameraPropertyFlag::Absolute) => V4L2_CID_FOCUS_ABSOLUTE, - CameraPropertyId::Gamma, None => V4L2_CID_GAMMA, - CameraPropertyId::Gain, None => V4L2_CID_GAIN, - CameraPropertyId::Hue, None => V4L2_CID_HUE, - CameraPropertyId::Hue, Some(CameraPropertyFlag::Automatic) => V4L2_CID_HUE_AUTO, - CameraPropertyId::Iris, Some(CameraPropertyFlag::Relative) => V4L2_CID_IRIS_RELATIVE, - CameraPropertyId::Iris, Some(CameraPropertyFlag::Absolute) => V4L2_CID_IRIS_ABSOLUTE, - CameraPropertyId::Saturation, None => V4L2_CID_SATURATION, - CameraPropertyId::Sharpness, None => V4L2_CID_SHARPNESS, - CameraPropertyId::Pan, Some(CameraPropertyFlag::Absolute) => V4L2_CID_PAN_ABSOLUTE, - CameraPropertyId::Pan, Some(CameraPropertyFlag::Relative) => V4L2_CID_PAN_RELATIVE, - // CameraPropertyId::Pan, None => V4L2_CID_PAN_ABSOLUTE, - // CameraPropertyId::Tilt, None => V4L2_CID_TILT_ABSOLUTE, - CameraPropertyId::Tilt, Some(CameraPropertyFlag::Absolute) => V4L2_CID_TILT_ABSOLUTE, - CameraPropertyId::Tilt, Some(CameraPropertyFlag::Relative) => V4L2_CID_TILT_RELATIVE, - // CameraPropertyId::Zoom, None => V4L2_CID_ZOOM_ABSOLUTE, - CameraPropertyId::WhiteBalance, None => V4L2_CID_WHITE_BALANCE_TEMPERATURE, - CameraPropertyId::WhiteBalance, Some(CameraPropertyFlag::Automatic) => V4L2_CID_AUTO_WHITE_BALANCE, - CameraPropertyId::WhiteBalance, Some(CameraPropertyFlag::Enable) => V4L2_CID_DO_WHITE_BALANCE, - CameraPropertyId::Zoom, Some(CameraPropertyFlag::Absolute) => V4L2_CID_ZOOM_ABSOLUTE, - CameraPropertyId::Zoom, Some(CameraPropertyFlag::Relative) => V4L2_CID_ZOOM_RELATIVE, - CameraPropertyId::Zoom, Some(CameraPropertyFlag::Continuous) => V4L2_CID_ZOOM_CONTINUOUS, - // CameraPropertyId::Iris, None => V4L2_CID_IRIS_ABSOLUTE, - -}, linux_id_to_str, str_to_linux_id); - -pub struct DeviceInner { - device: Device, -} - -impl DeviceInner { - pub fn new(index: usize) -> Result { - let device = Device::new(index).map_err(|why| NokhwaError::OpenDeviceError(index.to_string(), why.to_string()))?; - Ok(DeviceInner { device }) - } - - - pub fn resolutions(&self, fourcc: FourCC) -> Result, NokhwaError> { - let resolutions = self.device.enum_framesizes(fourcc.into()).map_err(|why| NokhwaError::GetPropertyError { property: "enum_framesizes".to_string(), error: why.to_string() })?.into_iter().map(|r| r.size.to_discrete().into_iter()).flatten().map(|res| Resolution::new(res.width, res.height) ).collect::>(); - Ok(resolutions) - } - - pub fn frame_rates(&self, fourcc: FourCC, resolution: Resolution) -> Result, NokhwaError> { - let frame_rates = match self.device.enum_frameintervals(fourcc, resolution.width(), resolution.height()) { - Ok(intervals) => intervals.into_iter().map(|x| match x.interval { - FrameIntervalEnum::Discrete(d) => vec![d], - FrameIntervalEnum::Stepwise(step) => { - let mut temp_vec = vec![]; - for rate in (step.min.numerator..step.max.numerator).step_by(step.step.numerator as usize) { - temp_vec.push(Fraction::new(rate, step.min.denominator)) - } - temp_vec +macro_rules! define_control_id_conv { + ( $($control_id:expr => $v4l_cid:expr ,)+ ) => { + fn control_id_to_cid(control_id: ControlId) -> Result { + match control_id { + $( + $control_id => Ok($v4l_cid) + )+ + ControlId::PlatformSpecific(specific_id) => { + u32::try_from(specific_id).map_err(|why| { + NokhwaError::ConversionError("ID must be a u32".to_string()) + }) } - }), - Err(why) => { - return Err(NokhwaError::GetPropertyError { property: "enum_frameintervals".to_string(), error: why.to_string() }) + _ => Err(NokhwaError::ConversionError("Could not match ID".to_string()) +) } - }.into_iter().flatten().map(|x| FrameRate::new(x.numerator, x.denominator)).collect::>(); - Ok(frame_rates) - } + } - pub fn properties(&self) -> CameraProperties { - - } - - pub fn inner(&self) -> &Device { - &self.device + fn cid_to_control_id(cid: u32) -> ControlId { + match cid { + $( + $v4l_cid => $control_id + )+ + other_id => ControlId::PlatformSpecific(other_id as u64) + } + } } } -pub struct StreamInner<'a> { - stream: MmapStream<'a> +define_control_id_conv!( + ControlId::FocusMode => V4L2_CID_FOCUS_AUTO, + ControlId::FocusAutoRange => V4L2_CID_AUTO_FOCUS_RANGE, + ControlId::FocusAbsolute => V4L2_CID_FOCUS_ABSOLUTE, + ControlId::FocusRelative => V4L2_CID_FOCUS_RELATIVE, + ControlId::FocusStatus => V4L2_CID_AUTO_FOCUS_STATUS, + + ControlId::ExposureMode => V4L2_CID_EXPOSURE_AUTO, + ControlId::ExposureBias => V4L2_CID_AUTO_EXPOSURE_BIAS, + ControlId::ExposureMetering => V4L2_CID_EXPOSURE_METERING, + ControlId::ExposureAbsolute =>V4L2_CID_EXPOSURE_ABSOLUTE, + + ControlId::IsoMode =>V4L2_CID_ISO_SENSITIVITY_AUTO, + ControlId::IsoSensitivity => V4L2_CID_ISO_SENSITIVITY, + + ControlId::ApertureAbsolute => V4L2_CID_IRIS_ABSOLUTE, + ControlId::ApertureRelative => V4L2_CID_IRIS_RELATIVE, + + ControlId::WhiteBalanceMode => V4L2_CID_AUTO_WHITE_BALANCE, + ControlId::WhiteBalanceTemperature => V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + + ControlId::ZoomContinuous => V4L2_CID_ZOOM_CONTINUOUS, + ControlId::ZoomRelative => V4L2_CID_ZOOM_RELATIVE, + ControlId::ZoomAbsolute => V4L2_CID_ZOOM_ABSOLUTE, + + ControlId::LightingMode => V4L2_CID_FLASH_LED_MODE, + ControlId::LightingStart => V4L2_CID_FLASH_STROBE, + ControlId::LightingStop => V4L2_CID_FLASH_STROBE_STOP, + ControlId::LightingStatus => V4L2_CID_FLASH_STROBE_STATUS, + + ControlId::Orientation => V4L2_CID_CAMERA_ORIENTATION, +); + +fn flags(flags: Flags) -> HashSet { + let mut output_flags = HashSet::new(); + + if flags.intersects(Flags::DISABLED) { + output_flags.insert(ControlFlags::Disabled); + } + if flags.intersects(Flags::GRABBED) { + output_flags.insert(ControlFlags::Busy); + } + if flags.intersects(Flags::READ_ONLY) { + output_flags.insert(ControlFlags::ReadOnly); + } + if flags.intersects(Flags::UPDATE) { + output_flags.insert(ControlFlags::CascadingUpdates); + } + if flags.intersects(Flags::SLIDER) { + output_flags.insert(ControlFlags::Slider); + } + if flags.intersects(Flags::WRITE_ONLY) { + output_flags.insert(ControlFlags::WriteOnly); + } + if flags.intersects(Flags::VOLATILE) { + output_flags.insert(ControlFlags::Volatile); + } + if flags.intersects(Flags::EXECUTE_ON_WRITE) { + output_flags.insert(ControlFlags::ExecuteOnWrite); + } + + output_flags } + +fn convert_description_to_ctrl_body(description: Description) -> Option { + let flags = flags(description.flags); + + let (descriptor, default) = match description.typ { + Type::Integer | Type::Integer64 => { + ( + ControlValueDescriptor::Integer(Range::new(description.minimum, description.maximum, Some(description.step as i64))), + Some(ControlValue::Integer(description.default)) + ) + } + Type::U8 => { + ( + ControlValueDescriptor::Integer(Range::new(0, u8::MAX_VALUE as i64, Some(description.step as i64))), + Some(ControlValue::Integer(description.default)) + ) + } + Type::U16 => { + ( + ControlValueDescriptor::Integer(Range::new(0, u16::MAX_VALUE as i64, Some(description.step as i64))), + Some(ControlValue::Integer(description.default)) + ) + } + Type::U32 => { + ( + ControlValueDescriptor::Integer(Range::new(0, u32::MAX_VALUE as i64, Some(description.step as i64))), + Some(ControlValue::Integer(description.default)) + ) + } + Type::String => { + ( + ControlValueDescriptor::String, + None, + ) + } + Type::Boolean => { + ( + ControlValueDescriptor::Boolean, + Some(ControlValue::Boolean(description.default != 0)) + ) + } + Type::Bitmask => { + ( + ControlValueDescriptor::BitMask, + Some(ControlValue::BitMask(description.default)) + ) + } + Type::IntegerMenu | Type::Menu => { + // We just initialize the values to Null for now. + // We fill it out later. + + // our keys + let descriptor = match description.items { + Some(items) => { + ControlValueDescriptor::Menu(items.into_iter().map(|(idx, menu_item)| { + (ControlValue::Integer(idx as i64), match menu_item { + MenuItem::Name(name) => ControlValue::String(name), + MenuItem::Value(v) => ControlValue::Integer(*v), + }) + }).collect::>()) + } + // This can probably never happen so we just immediately return if this bad thing + // happens somehow + None => return None, + }; + ( + descriptor, + Some(ControlValue::Integer(description.default)) + ) + } + Type::Button => { + ( + ControlValueDescriptor::Null, + None, + ) + } + + // we simply will not support control class. + // if someone needs it we can fix it later. + // honestly the whole concept scares me. + // i also have no idea on what an Area could be + // v4l2 docs are very sparse with this info. https://docs.kernel.org/userspace-api/media/v4l/ext-ctrls-image-source.html#c.v4l2_area + _ => return None, + }; + + Some( + ControlBody::new( + flags, + descriptor, + default + ) + ) +} + + +pub struct V4L2Platform {} + +impl PlatformTrait for V4L2Platform { + const PLATFORM: Backends = Backends::Video4Linux2; + type Camera = V4L2Camera; + + fn block_on_permission(&mut self) -> NokhwaResult<()> { + Ok(()) + } + + fn check_permission_given(&mut self) -> bool { + true + } + + fn query(&mut self) -> NokhwaResult> { + Ok(enum_devices().into_iter() + .map(|v4l_node| { + let index = v4l_node.index(); + // open camera for capabilities. if we dont get any, dont return the camera + Device::new(index).map(|dev| + dev.query_caps().map(|caps| { + index_capabilities_to_camera_info(index as u32, caps) + }).ok() + ).ok().flatten() + }).flatten().collect::>()) + } + + fn open(&mut self, index: &CameraIndex) -> NokhwaResult { + todo!() + } +} + +pub struct V4L2Camera { + device: Device, + camera_format: Option, + camera_index: CameraIndex, + controls: Option, +} + +impl Setting for V4L2Camera { + fn enumerate_formats(&self) -> Result, NokhwaError> { + let mut formats = vec![]; + + for frame_format in self.device.enum_formats().map_err(|why| { + NokhwaError::GetPropertyError { property: "enum_formats".to_string(), error: why.to_string() } + })?.into_iter().map(|desc| { + fourcc_to_frame_format(desc.fourcc) + }) { + formats.extend( + self.enumerate_resolution_and_frame_rates(frame_format)?.into_iter().flat_map(|(resolution, frame_rates)| { + frame_rates.into_iter().map(|frame_rate| { + CameraFormat::new(resolution, frame_format, frame_rate) + }) + }) + ); + } + Ok(formats) + } + + fn enumerate_resolution_and_frame_rates(&self, frame_format: FrameFormat) -> Result>, NokhwaError> { + let fourcc = frame_format_to_fourcc(frame_format)?; + let resolutions = self.device.enum_framesizes(fourcc).map_err(|why| { + NokhwaError::GetPropertyError { property: "enum_framesizes".to_string(), error: why.to_string() } + })?.into_iter() + .flat_map(|frame_size| { + frame_size.size.to_discrete() + }).map(|discrete| { Resolution::new(discrete.width, discrete.height) + }).collect::>(); + + let v4l2_frame_intervals = resolutions.iter() + .map(|resolution| (*resolution, self.device.enum_frameintervals(fourcc, resolution.width(), resolution.height()))) + .collect::)>, std::io::Error>>() + .map_err(|why| { + NokhwaError::GetPropertyError { property: "enum_frameintervals".to_string(), error: why.to_string() } + })?; + + Ok(v4l2_frame_intervals.into_iter().flatten().flat_map(|(resolution, interval)| { + match interval.interval { + FrameIntervalEnum::Discrete(discrete) => { + NonZeroI32::new(discrete.denominator as i32).map(|denominator| { + (resolution, vec![FrameRate::new(discrete.numerator as i32, denominator)]) + }) + } + FrameIntervalEnum::Stepwise(stepwise) => { + // we have to do this ourselves + + // no logic to handle different or zero demoninator + if (stepwise.step.denominator != stepwise.max.denominator) || (stepwise.step.denominator != stepwise.min.denominator) { + return None + } + + let min = stepwise.min.numerator as i32; + let max = stepwise.max.numerator as i32; + let step = stepwise.step.numerator as i32; + let denominator = stepwise.step.denominator as i32; + + NonZeroI32::new(denominator).map(|denominator| { + (resolution, (min..max).step_by(step as usize).map(|numerator| { + FrameRate::new(numerator, denominator) + }).collect::>()) + }) + } + } + }).flatten().collect::>>()) + } + + fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError> { + let fourcc = frame_format_to_fourcc(*camera_format.format())?; + self.device.set_format( + &Format::new(camera_format.width(), camera_format.height(), fourcc) + ).map_err(|why| NokhwaError::SetPropertyError { + property: "set_format".to_string(), + value: format!("format: {camera_format} fourcc: {fourcc}"), + error: why.to_string(), + })?; + self.device.set_params(&Parameters::new(Fraction::new(*camera_format.frame_rate().numerator() as u32, *camera_format.frame_rate().denominator() as u32))).map_err(|why| { + NokhwaError::SetPropertyError { + property: "set_params".to_string(), + value: format!("{}", camera_format.frame_rate()), + error: why.to_string(), + } + })?; + Ok(()) + } + + fn controls(&self) -> &Controls { + + match self.controls { + + } + + let properties = self.device.query_controls().map_err(|why| { + NokhwaError::GetPropertyError { property: "query_controls".to_string(), error: why.to_string() } + })?.into_iter().map(|description| { + let id = cid_to_control_id(description.id); + + convert_description_to_ctrl_body(description).map(|body| { + (id, body) + }) + }).flatten().collect::>(); + } + + fn set_control(&mut self, property: &ControlId, value: ControlValue) -> Result<(), NokhwaError> { + todo!() + } +} + +impl Capture for V4L2Camera { + fn open_stream(&mut self) -> Result { + todo!() + } + + fn close_stream(&mut self) -> Result<(), NokhwaError> { + todo!() + } +} + +impl Camera for V4L2Camera {} diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index a9e258e..6d230d6 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -246,7 +246,7 @@ mod internal { ffi::{c_float, c_void, CStr}, sync::Arc, }; - use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; + use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; const UTF8_ENCODING: usize = 4; type CGFloat = c_float; diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index 890f36c..50ddae4 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -46,7 +46,7 @@ pub mod wmf { Arc, }, }; - use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; + use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; use windows::Win32::Media::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual}; use windows::Win32::Media::MediaFoundation::{ IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM, @@ -1229,7 +1229,7 @@ pub mod wmf { CameraFormat, CameraIndex, CameraInformation, }; use std::borrow::Cow; - use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; + use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; pub fn initialize_mf() -> Result<(), NokhwaError> { Err(NokhwaError::NotImplementedError( diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index f40b43a..69b23be 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -25,6 +25,8 @@ thiserror = "2.0" bytes = "1.3" flume = "0.11" num-traits = "0.2" +derive_builder = "0.20" +ordered-float = "4.6" [dependencies.num-rational] version = "0.4" @@ -57,8 +59,5 @@ optional = true version = "0.3" optional = true -[dependencies.rgb] -version = "0.8" - [package.metadata.docs.rs] features = ["docs-features"] diff --git a/nokhwa-core/src/camera.rs b/nokhwa-core/src/camera.rs index 9e9ae0f..6cd718f 100644 --- a/nokhwa-core/src/camera.rs +++ b/nokhwa-core/src/camera.rs @@ -1,6 +1,6 @@ use crate::error::{NokhwaError}; use crate::frame_format::FrameFormat; -use crate::properties::{ControlId, ControlValue, Properties}; +use crate::control::{ControlId, ControlValue, Controls}; use crate::types::{CameraFormat, FrameRate, Resolution}; use std::collections::HashMap; use crate::stream::Stream; @@ -15,9 +15,11 @@ pub trait Setting { fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - fn properties(&self) -> &Properties; + fn controls(&self) -> &Controls; - fn set_property( + fn refresh_controls(&mut self) -> Result<(), NokhwaError>; + + fn set_control( &mut self, property: &ControlId, value: ControlValue, @@ -36,7 +38,7 @@ pub trait AsyncSetting { async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - async fn properties_async(&self) -> &Properties; + async fn properties_async(&self) -> &Controls; async fn set_property_async( &mut self, diff --git a/nokhwa-core/src/control.rs b/nokhwa-core/src/control.rs new file mode 100644 index 0000000..8d0f475 --- /dev/null +++ b/nokhwa-core/src/control.rs @@ -0,0 +1,425 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::{Display, Formatter}; +use std::hash::{Hash, Hasher}; +use ordered_float::OrderedFloat; +use crate::error::{NokhwaError, NokhwaResult}; +use crate::ranges::{Range, ValidatableRange}; + +pub type PlatformSpecificControlId = u64; + +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub enum ControlId { + FocusMode, + FocusAutoType, + FocusAutoRange, + FocusAbsolute, + FocusRelative, + FocusStatus, + + ExposureMode, + ExposureBias, + ExposureMetering, + ExposureAbsolute, + ExposureRelative, + + IsoMode, + IsoSensitivity, + + ApertureAbsolute, + ApertureRelative, + + WhiteBalanceMode, + WhiteBalanceTemperature, + + ZoomContinuous, + ZoomRelative, + ZoomAbsolute, + + LightingMode, + LightingStart, + LightingStop, + LightingStatus, + + Orientation, + + PlatformSpecific(PlatformSpecificControlId) +} + +impl Display for ControlId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Control ID: {self:?}") + } +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Controls { + controls: HashMap, + values: HashMap, +} + +impl Controls { + pub fn new(device_controls: HashMap) -> Self { + Self { + controls: device_controls, + } + } + + pub fn empty() -> Self { + Self::default() + } + + pub fn control_value(&self, control_id: &ControlId) -> Option<&ControlBody> { + self.controls.get(control_id) + } + + pub fn set_control_value(&mut self, control_id: &ControlId, value: ControlValue) -> NokhwaResult<()> { + // see if it exists + if let Some(control) = self.controls.get_mut(control_id) { + // FIXME: Remove this clone one day! + control.set_value(value.clone())?; + } + Err(NokhwaError::SetPropertyError { + property: control_id.to_string(), + value: value.to_string(), + error: "Not Found/Not Supported".to_string(), + }) + } +} + + +#[derive(Clone, Debug, PartialEq)] +pub struct ControlBody { + flags: HashSet, + descriptor: ControlValueDescriptor, + default_value: Option, +} + +impl ControlBody { + pub fn new(control_flags: HashSet, control_value_descriptor: ControlValueDescriptor, default_value: Option) -> Self { + Self { + flags: control_flags, + descriptor: control_value_descriptor, + default_value, + } + } + + pub fn flags(&self) -> &HashSet { + &self.flags + } + + pub fn descriptor(&self) -> &ControlValueDescriptor { + &self.descriptor + } + + pub fn default_value(&self) -> &Option { + &self.default_value + } + + pub fn add_flag(&mut self, flag: ControlFlags) { + self.flags.insert(flag); + } + + pub fn remove_flag(&mut self, flag: ControlFlags) -> bool { + self.flags.remove(&flag) + } +} + +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub enum ControlFlags { + Disabled, + Busy, + ReadOnly, + CascadingUpdates, + Inactive, + Slider, + WriteOnly, + Volatile, + ContinuousChange, + ExecuteOnWrite, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ControlValueDescriptor { + Null, + Integer(Range), + BitMask, + Float(Range), + String, + Boolean, + // Array of any values of singular type + Array(ControlValueDescriptor), + // Menu(Enum) of valid choices + // The keys are valid choices, + // the values represent what the choice is (usually a string or int). + Menu(HashMap), + // lmao u deal with it + // max/min length set by range. step is ALWAYS zero. + Binary(Range), + // An area (Resolution) like type + Area { + width_limits: Range, + height_limits: Range, + }, +} + +impl ControlValueDescriptor { + pub fn validate(&self, value: &ControlValue) -> bool { + match self { + ControlValueDescriptor::Null => { + if let &ControlValue::Null = value { + return false + } + } + ControlValueDescriptor::Integer(int_range) => { + if let ControlValue::Integer(i) = value { + return int_range.validate(i) + } + } + ControlValueDescriptor::BitMask => { + if let &ControlValue::BitMask(_) = value { + return true + } + } + ControlValueDescriptor::Float(float_range) => { + if let ControlValue::Float(i) = value { + return float_range.validate(i) + } + } + ControlValueDescriptor::String => { + if let &ControlValue::String(_) = value { + return true + } + } + ControlValueDescriptor::Boolean => { + if let &ControlValue::Boolean(_) = value { + return true + } + } + ControlValueDescriptor::Array(arr) => { + if let &ControlValue::Array(_) = value { + return arr.is_valid_value(value) + } + } + ControlValueDescriptor::Binary(size_limits) => { + if let ControlValue::Binary(bin) = value { + return size_limits.validate(bin.len() as u64) + } + } + ControlValueDescriptor::Menu(choices) => { + if let ControlValue::EnumPick(choice) = value { + return choices.contains_key(choice) + } + } + ControlValueDescriptor::Area { width_limits, height_limits } => { + if let ControlValue::Area { width, height } = &value { + return width_limits.validate(width) && height_limits.validate(height) + } + } + } + false + } +} +// +// #[derive(Clone, Debug, PartialEq)] +// pub enum ControlValuePrimitiveDescriptor { +// Null, +// Integer(Range), +// BitMask, +// Float(Range), +// String, +// Binary, +// Boolean, +// } +// +// impl ControlValuePrimitiveDescriptor { +// pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool { +// match self { +// ControlValuePrimitiveDescriptor::Null => { +// if let ControlValuePrimitive::Null = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Integer(i) => { +// if let ControlValuePrimitive::Integer(v) = other { +// return i.validate(v) +// } +// } +// ControlValuePrimitiveDescriptor::BitMask => { +// if let ControlValuePrimitive::BitMask(_) = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Float(f) => { +// if let ControlValuePrimitive::Float(v) = other { +// return f.validate(v) +// } +// } +// ControlValuePrimitiveDescriptor::String => { +// if let ControlValuePrimitive::String(_) = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Boolean => { +// if let ControlValuePrimitive::Boolean(_) = other { +// return true +// } +// } +// } +// false +// } +// +// pub fn is_valid_value(&self, other: &ControlValue) -> bool { +// match self { +// ControlValuePrimitiveDescriptor::Null => { +// if let ControlValue::Null = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Integer(i) => { +// if let ControlValue::Integer(v) = other { +// return i.validate(v) +// } +// } +// ControlValuePrimitiveDescriptor::BitMask => { +// if let ControlValue::BitMask(_) = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Float(f) => { +// if let ControlValue::Float(v) = other { +// return f.validate(v) +// } +// } +// ControlValuePrimitiveDescriptor::String => { +// if let ControlValue::String(_) = other { +// return true +// } +// } +// ControlValuePrimitiveDescriptor::Boolean => { +// if let ControlValue::Boolean(_) = other { +// return true +// } +// } +// } +// false +// } +// } +// +// #[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +// pub enum ControlValuePrimitive { +// Null, +// Integer(i64), +// BitMask(i64), +// Float(OrderedFloat), +// String(String), +// Boolean(bool), +// } +// +// impl From for ControlValue { +// fn from(value: ControlValuePrimitive) -> Self { +// match value { +// ControlValuePrimitive::Null => ControlValue::Null, +// ControlValuePrimitive::Integer(i) => ControlValue::Integer(i), +// ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b), +// ControlValuePrimitive::Float(f) => ControlValue::Float(f), +// ControlValuePrimitive::String(s) => ControlValue::String(s), +// ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b), +// } +// } +// } + +#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd)] +pub enum ControlValue { + Null, + Integer(i64), + BitMask(i64), + Float(OrderedFloat), + String(String), + Boolean(bool), + Array(Vec), + Binary(Vec), + EnumPick(Box), + Area { + width: i64, + height: i64, + } +} + +impl ControlValue { + pub fn is_primitive(&self) -> bool { + match self { + ControlValue::Null | + ControlValue::Integer(_) | + ControlValue::BitMask(_) | + ControlValue::Float(_) | + ControlValue::String(_)| + ControlValue::Boolean(_) | + ControlValue::Binary(_) | + ControlValue::Area { .. } => true, + _ => false, + } + } + + // pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool { + // match other { + // ControlValuePrimitive::Null => { + // if let ControlValue::Null = self { + // return true + // } + // } + // ControlValuePrimitive::Integer(_) => {if let ControlValue::Integer(_) = self {return true}} + // ControlValuePrimitive::BitMask(_) => {if let ControlValue::BitMask(_) = self {return true}} + // ControlValuePrimitive::Float(_) => {if let ControlValue::Float(_) = self {return true}} + // ControlValuePrimitive::String(_) => {if let ControlValue::String(_) = self {return true}} + // ControlValuePrimitive::Boolean(_) => {if let ControlValue::Boolean(_) = self {return true}} + // } + // false + // } + + pub fn same_type(&self, other: &ControlValue) -> bool { + match self { + ControlValue::Null => { + if let ControlValue::Null = other { + return true; + } + } + ControlValue::Integer(_) => {if let ControlValue::Integer(_) = other { + return true; + }} + ControlValue::BitMask(_) => {if let ControlValue::BitMask(_) = other { + return true; + }} + ControlValue::Float(_) => {if let ControlValue::Float(_) = other { + return true; + }} + ControlValue::String(_) => {if let ControlValue::String(_) = other { + return true; + }} + ControlValue::Boolean(_) => {if let ControlValue::Boolean(_) = other { + return true; + }} + ControlValue::Array(_) => {if let ControlValue::Array(_) = other { + return true; + }} + ControlValue::EnumPick(_) => {if let ControlValue::EnumPick(_) = other { + return true; + }} + ControlValue::Binary(_) => {if let ControlValue::Binary(_) = other { + return true; + }} + ControlValue::Area { .. } => {if let ControlValue::Area { .. } = other { + return true; + }} + } + + false + } + + +} + +impl Display for ControlValue { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Control Value: {self:?}") + } +} diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index dc494cb..447dd0f 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -24,8 +24,6 @@ pub type NokhwaResult = Result; #[allow(clippy::module_name_repetitions)] #[derive(Error, Debug, Clone)] pub enum NokhwaError { - #[error("Unitialized Camera. Call `init()` first!")] - UnitializedError, #[error("Could not initialize {backend}: {error}")] InitializeError { backend: Backends, error: String }, #[error("Could not shutdown {backend}: {error}")] diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 8f85cdf..b1c36d5 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -63,11 +63,11 @@ pub enum FrameFormat { // RGB Formats Rgb332, - Rgb555, - Rgb565, - Rgb888, + Bgr888, + BgrA8888, + RgbA8888, ARgb8888, diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index b9ed59a..4f9e163 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -28,7 +28,7 @@ pub mod error; pub mod format_request; pub mod frame_buffer; pub mod frame_format; -pub mod properties; +pub mod control; pub mod ranges; pub mod traits; pub mod types; diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index dd51151..9a458ff 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -31,6 +31,10 @@ pub trait PlatformTrait { fn query(&mut self) -> NokhwaResult>; fn open(&mut self, index: &CameraIndex) -> NokhwaResult; + + fn open_dynamic(&mut self, index: &CameraIndex) -> NokhwaResult> { + self.open(index).map(|cam| Box::new(cam)) + } } #[cfg(feature = "async")] @@ -45,4 +49,9 @@ pub trait AsyncPlatformTrait { async fn query_async(&mut self) -> NokhwaResult>; async fn open_async (&mut self, index: &CameraIndex) -> NokhwaResult; + + + async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult> { + self.open_async(index).await.map(|cam| Box::new(cam)) + } } \ No newline at end of file diff --git a/nokhwa-core/src/properties.rs b/nokhwa-core/src/properties.rs deleted file mode 100644 index 192d600..0000000 --- a/nokhwa-core/src/properties.rs +++ /dev/null @@ -1,446 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::fmt::{Display, Formatter}; -use crate::error::{NokhwaError, NokhwaResult}; -use crate::ranges::{Range, ValidatableRange}; - -pub type PlatformSpecificControlId = u64; - -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum ControlId { - FocusMode, - FocusAutoType, - FocusAutoRange, - FocusAbsolute, - FocusRelative, - FocusStatus, - - ExposureMode, - ExposureBias, - ExposureTime, - ExposureAutoPriority, - ExposureIsoMode, - ExposureIsoSensitivity, - ExposureApertureAbsolute, - ExposureApertureRelative, - - WhiteBalanceMode, - WhiteBalanceTemperature, - - ZoomMode, - LightingMode, - PlatformSpecific(PlatformSpecificControlId) -} - -impl Display for ControlId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Control ID: {self:?}") - } -} - -#[derive(Clone, Debug, Default, PartialEq)] -pub struct Properties { - controls: HashMap, -} - -impl Properties { - pub fn new(device_controls: HashMap) -> Self { - Self { - controls: device_controls, - } - } - - pub fn empty() -> Self { - Self::default() - } - - pub fn control_value(&self, control_id: &ControlId) -> Option<&ControlBody> { - self.controls.get(control_id) - } - - pub fn set_control_value(&mut self, control_id: &ControlId, value: ControlValue) -> NokhwaResult<()> { - // see if it exists - if let Some(control) = self.controls.get_mut(control_id) { - // FIXME: Remove this clone one day! - control.set_value(value.clone())?; - } - Err(NokhwaError::SetPropertyError { - property: control_id.to_string(), - value: value.to_string(), - error: "Not Found/Not Supported".to_string(), - }) - } -} - - -#[derive(Clone, Debug, PartialEq)] -pub struct ControlBody { - control_type: ControlType, - flags: HashSet, - descriptor: ControlValueDescriptor, - value: Option, - default_value: Option, -} - -impl ControlBody { - pub fn new(control_type: ControlType, control_flags: HashSet, control_value_descriptor: ControlValueDescriptor, value: Option, default_value: Option) -> Self { - Self { - control_type, - flags: control_flags, - descriptor: control_value_descriptor, - value, - default_value, - } - } - - pub fn control_type(&self) -> &ControlType { - &self.control_type - } - - pub fn flags(&self) -> &HashSet { - &self.flags - } - - pub fn descriptor(&self) -> &ControlValueDescriptor { - &self.descriptor - } - - pub fn value(&self) -> &Option { - &self.value - } - - pub fn default_value(&self) -> &Option { - &self.default_value - } - - pub fn add_flag(&mut self, flag: ControlFlags) { - self.flags.insert(flag); - } - - pub fn remove_flag(&mut self, flag: ControlFlags) -> bool { - self.flags.remove(&flag) - } - - pub fn set_value(&mut self, value: ControlValue) -> NokhwaResult> { - if self.descriptor.validate(&value) { - return Err(NokhwaError::SetPropertyError { - property: "Control Body".to_string(), - value: value.to_string(), - error: "Failed to validate control value".to_string(), - }) - } - - let old = core::mem::replace(&mut self.value, Some(value)); - Ok(old) - } - - pub fn clear_value(&mut self) -> Option { - core::mem::replace(&mut self.value, None) - } - - -} - -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum ControlType { - Button, - Integer, - Menu, - IntegerMenu, - BinaryMenu, - Bitmask, - String, -} - -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum ControlFlags { - Disabled, - Busy, - ReadOnly, - CascadingUpdates, - Inactive, - Slider, - WriteOnly, - ContinuousChange, - ExecuteOnWrite, -} - -#[derive(Clone, Debug, PartialEq)] -pub enum ControlValueDescriptor { - Null, - Integer(Range), - BitMask, - Float(Range), - String, - Boolean, - // Array of any values of singular type - Array(ControlValuePrimitiveDescriptor), - // Multiple Choice from array - MultiChoice(Vec), - // Singular Choice from array - Enum(Vec), - // Hashmap - Map(HashMap), - // A menu, where you pick a key-value - Menu(HashMap) -} - -impl ControlValueDescriptor { - pub fn validate(&self, value: &ControlValue) -> bool { - match self { - ControlValueDescriptor::Null => { - if let &ControlValue::Null = value { - return false - } - } - ControlValueDescriptor::Integer(int_range) => { - if let ControlValue::Integer(i) = value { - return int_range.validate(i) - } - } - ControlValueDescriptor::BitMask => { - if let &ControlValue::BitMask(_) = value { - return true - } - } - ControlValueDescriptor::Float(float_range) => { - if let ControlValue::Float(i) = value { - return float_range.validate(i) - } - } - ControlValueDescriptor::String => { - if let &ControlValue::String(_) = value { - return true - } - } - ControlValueDescriptor::Boolean => { - if let &ControlValue::Boolean(_) = value { - return true - } - } - ControlValueDescriptor::Array(arr) => { - if let &ControlValue::Array(_) = value { - return arr.is_valid_value(value) - } - } - ControlValueDescriptor::MultiChoice(choices) => { - if let ControlValue::Array(values) = value { - for v in values { - let mut contains = false; - let vl: ControlValue = v.clone().into(); - for choice in choices { - if choice.is_valid_value(&vl) { - contains = true; - break; - } - } - return contains - } - } - } - ControlValueDescriptor::Enum(choices) => { - for choice in choices { - return choice.is_valid_value(&value) - } - } - ControlValueDescriptor::Map(map) => { - if let ControlValue::Map(setting_map) = &value { - for (setting_key, setting_value) in setting_map { - if let Some(descriptor) = map.get(setting_key) { - return !descriptor.is_valid_primitive_value(setting_value) - } - } - } - } - ControlValueDescriptor::Menu(menu) => { - if let ControlValue::KeyValue(k, v) = &value { - if let Some(descriptor) = menu.get(k) { - return descriptor.is_valid_primitive_value(v) - } - } - } - } - false - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum ControlValuePrimitiveDescriptor { - Null, - Integer(Range), - BitMask, - Float(Range), - String, - Boolean, -} - -impl ControlValuePrimitiveDescriptor { - pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool { - match self { - ControlValuePrimitiveDescriptor::Null => { - if let ControlValuePrimitive::Null = other { - return true - } - } - ControlValuePrimitiveDescriptor::Integer(i) => { - if let ControlValuePrimitive::Integer(v) = other { - return i.validate(v) - } - } - ControlValuePrimitiveDescriptor::BitMask => { - if let ControlValuePrimitive::BitMask(_) = other { - return true - } - } - ControlValuePrimitiveDescriptor::Float(f) => { - if let ControlValuePrimitive::Float(v) = other { - return f.validate(v) - } - } - ControlValuePrimitiveDescriptor::String => { - if let ControlValuePrimitive::String(_) = other { - return true - } - } - ControlValuePrimitiveDescriptor::Boolean => { - if let ControlValuePrimitive::Boolean(_) = other { - return true - } - } - } - false - } - - pub fn is_valid_value(&self, other: &ControlValue) -> bool { - match self { - ControlValuePrimitiveDescriptor::Null => { - if let ControlValue::Null = other { - return true - } - } - ControlValuePrimitiveDescriptor::Integer(i) => { - if let ControlValue::Integer(v) = other { - return i.validate(v) - } - } - ControlValuePrimitiveDescriptor::BitMask => { - if let ControlValue::BitMask(_) = other { - return true - } - } - ControlValuePrimitiveDescriptor::Float(f) => { - if let ControlValue::Float(v) = other { - return f.validate(v) - } - } - ControlValuePrimitiveDescriptor::String => { - if let ControlValue::String(_) = other { - return true - } - } - ControlValuePrimitiveDescriptor::Boolean => { - if let ControlValue::Boolean(_) = other { - return true - } - } - } - false - } -} - -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub enum ControlValuePrimitive { - Null, - Integer(i64), - BitMask(i64), - Float(f64), - String(String), - Boolean(bool), -} - -impl From for ControlValue { - fn from(value: ControlValuePrimitive) -> Self { - match value { - ControlValuePrimitive::Null => ControlValue::Null, - ControlValuePrimitive::Integer(i) => ControlValue::Integer(i), - ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b), - ControlValuePrimitive::Float(f) => ControlValue::Float(f), - ControlValuePrimitive::String(s) => ControlValue::String(s), - ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b), - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum ControlValue { - Null, - Integer(i64), - BitMask(i64), - Float(f64), - String(String), - Boolean(bool), - Array(Vec), - KeyValue(String, ControlValuePrimitive), - Map(HashMap), -} - -impl ControlValue { - pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool { - match other { - ControlValuePrimitive::Null => { - if let ControlValue::Null = self { - return true - } - } - ControlValuePrimitive::Integer(_) => {if let ControlValue::Integer(_) = self {return true}} - ControlValuePrimitive::BitMask(_) => {if let ControlValue::BitMask(_) = self {return true}} - ControlValuePrimitive::Float(_) => {if let ControlValue::Float(_) = self {return true}} - ControlValuePrimitive::String(_) => {if let ControlValue::String(_) = self {return true}} - ControlValuePrimitive::Boolean(_) => {if let ControlValue::Boolean(_) = self {return true}} - } - false - } - - pub fn same_type(&self, other: &ControlValue) -> bool { - match self { - ControlValue::Null => { - if let ControlValue::Null = other { - return true; - } - } - ControlValue::Integer(_) => {if let ControlValue::Integer(_) = other { - return true; - }} - ControlValue::BitMask(_) => {if let ControlValue::BitMask(_) = other { - return true; - }} - ControlValue::Float(_) => {if let ControlValue::Float(_) = other { - return true; - }} - ControlValue::String(_) => {if let ControlValue::String(_) = other { - return true; - }} - ControlValue::Boolean(_) => {if let ControlValue::Boolean(_) = other { - return true; - }} - ControlValue::Array(_) => {if let ControlValue::Array(_) = other { - return true; - }} - ControlValue::KeyValue(_, _) => {if let ControlValue::KeyValue(_, _) = other { - return true; - }} - ControlValue::Map(_) => {if let ControlValue::Map(_) = other { - return true; - }} - } - - false - } - - -} - -impl Display for ControlValue { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Control Value: {self:?}") - } -} diff --git a/nokhwa-core/src/ranges.rs b/nokhwa-core/src/ranges.rs index 253c904..3250a59 100644 --- a/nokhwa-core/src/ranges.rs +++ b/nokhwa-core/src/ranges.rs @@ -1,13 +1,7 @@ -use crate::error::NokhwaError; use core::fmt::{Debug, Display, Formatter}; -use std::collections::hash_map::Keys; -use std::collections::HashMap; use std::hash::Hash; use std::ops::{Div, Rem, Sub}; - -/// Failed to validate. -#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct RangeValidationFailure; +use ordered_float::OrderedFloat; /// A range type that can be validated. pub trait ValidatableRange { @@ -21,34 +15,32 @@ pub trait ValidatableRange { /// Creates a range of values. /// /// Inclusive by default. -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub struct Range { - minimum: Option, +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct Range where T: RangeItem +{ + minimum: T, lower_inclusive: bool, - maximum: Option, + maximum: T, upper_inclusive: bool, - preferred: T, step: Option, } impl Range where T: Copy { /// Create an upper and lower inclusive [`Range`] - pub fn new(preferred: T, min: Option, max: Option, step: Option) -> Self { + pub fn new(min: T, max: T, step: Option) -> Self { Self { minimum: min, lower_inclusive: true, maximum: max, upper_inclusive: true, - preferred, step, } } pub fn with_inclusive( - preferred: T, - min: Option, + min: T, lower_inclusive: bool, - max: Option, + max: T, upper_inclusive: bool, step: Option ) -> Self { @@ -57,22 +49,10 @@ impl Range where T: Copy { lower_inclusive, maximum: max, upper_inclusive, - preferred, step, } } - pub fn exact(preferred: T) -> Self { - Self { - minimum: None, - lower_inclusive: true, - maximum: None, - upper_inclusive: true, - preferred, - step: None, - } - } - pub fn set_minimum(&mut self, minimum: Option) { self.minimum = minimum; } @@ -88,24 +68,18 @@ impl Range where T: Copy { pub fn set_step(&mut self, step: T) { self.step = Some(step); } - pub fn set_preferred(&mut self, preferred: T) { - self.preferred = preferred; - } - pub fn minimum(&self) -> Option { + pub fn minimum(&self) -> T { self.minimum } pub fn lower_inclusive(&self) -> bool { self.lower_inclusive } - pub fn maximum(&self) -> Option { + pub fn maximum(&self) -> T { self.maximum } pub fn upper_inclusive(&self) -> bool { self.upper_inclusive } - pub fn preferred(&self) -> T { - self.preferred - } pub fn step(&self) -> Option { self.step } @@ -113,20 +87,32 @@ impl Range where T: Copy { impl ValidatableRange for Range where - T: SimpleRangeItem, + T: RangeItem, { type Validation = T; fn validate(&self, value: &T) -> bool { - num_range_validate( - self.minimum, - self.maximum, - self.preferred, - self.lower_inclusive, - self.upper_inclusive, - self.step, - *value, - ) + let l_comparison_fn = match self.lower_inclusive { + true => T::ge, + false => T::gt, + }; + let u_comparison_fn = match self.upper_inclusive { + true => T::le, + false => T::lt, + }; + + if !(l_comparison_fn(&self.minimum, value) && u_comparison_fn(&self.maximum, value)) { + return false + } + + // check step + + if let Some(step) = self.step { + let step_chk_value = *value - self.minimum; + return step_chk_value % step == 0; + } + + return true } } @@ -136,11 +122,10 @@ where { fn default() -> Self { Range { - minimum: None, + minimum: T::default(), lower_inclusive: true, - maximum: None, + maximum: T::default(), upper_inclusive: true, - preferred: T::default(), step: None, } } @@ -153,198 +138,15 @@ where fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let lower_inclusive_char = bool_to_inclusive_char(self.lower_inclusive, false); let upper_inclusive_char = bool_to_inclusive_char(self.upper_inclusive, true); - let default = &self.preferred; write!( f, - "Range: {lower_inclusive_char}{:?}, {:?}{upper_inclusive_char}, Preferred: {default:?}", + "Range: {lower_inclusive_char}{:?}, {:?}{upper_inclusive_char}", self.minimum, self.maximum ) } } -#[derive(Clone, Debug)] -pub struct Options { - default: Option, - available: Vec, -} - -impl Options -where - T: Clone + Debug + PartialEq, -{ - pub fn new(values: Vec, default_value: Option) -> Self { - Self { - default: default_value, - available: values, - } - } - - pub fn default_value(&self) -> Option<&T> { - self.default.as_ref() - } - - pub fn available(&self) -> &[T] { - &self.available - } -} - -impl ValidatableRange for Options -where - T: Clone + PartialEq, -{ - type Validation = T; - - fn validate(&self, value: &Self::Validation) -> bool { - self.available.contains(value) - } -} - -impl Display for Options -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let default = default_to_string(&self.default); - - write!( - f, - "Options: Available {:?}, Default: {default}", - self.available - ) - } -} - -#[derive(Clone, Debug)] -pub struct KeyValue -where - K: Clone + Debug + Hash + Eq, - V: Clone + Debug, -{ - defaults: HashMap, -} - -impl KeyValue -where - K: Clone + Debug + Hash + Eq, - V: Clone + Debug, -{ - pub fn new(default: HashMap) -> Self { - Self { defaults: default } - } - - pub fn available_keys(&self) -> Keys<'_, K, V> { - self.defaults.keys() - } - - pub fn by_key(&self, key: &K) -> Option<&V> { - self.defaults.get(key) - } -} - -impl Display for KeyValue -where - K: Clone + Debug + Hash + Eq, - V: Clone + Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - // TODO: pretty print? - write!(f, "Key Value Pairs: {:?}", self.defaults) - } -} - -#[derive(Clone, Debug)] -pub struct ArrayRange { - appendable_options: Vec, - default_options: Vec, -} - -impl ArrayRange -where - T: Clone + Debug + PartialEq, -{ - pub fn new(appendable: Vec, default: Vec) -> Result { - for option in &default { - if !appendable.contains(option) { - return Err(NokhwaError::StructureError { structure: "ArrayRange".to_string(), error: "Attempted to add an undependable option to default option - ILLEGAL! - If you got this while using a driver, this is a bug! Please report to https://github.com/l1npengtul/nokhwa/issues!".to_string() }); - } - } - - Ok(Self { - appendable_options: appendable, - default_options: default, - }) - } - - pub fn appendable_options(&self) -> &[T] { - &self.appendable_options - } - - pub fn default_options(&self) -> &[T] { - &self.default_options - } -} - -impl ValidatableRange for ArrayRange -where - T: PartialEq, -{ - type Validation = T; - - fn validate(&self, value: &Self::Validation) -> bool { - self.appendable_options.contains(value) - } -} - -impl Display for ArrayRange -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "ArrayRange: Available Options: {:?}, Default: {:?}", - self.appendable_options, self.default_options - ) - } -} - -#[derive(Clone, Debug)] -pub struct Simple { - default: Option, -} - -impl Simple -where - T: Clone + Debug, -{ - pub fn new(default: Option) -> Self { - Self { default } - } - - pub fn default_value(&self) -> Option<&T> { - self.default.as_ref() - } -} - -impl ValidatableRange for Simple { - type Validation = T; - - fn validate(&self, _: &Self::Validation) -> bool { - true - } -} - -impl Display for Simple -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let default = default_to_string(&self.default); - write!(f, "Simple (Any Value): Default Value: {default}") - } -} - fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char { match inclusive { true => { @@ -376,67 +178,13 @@ where } } -fn num_range_validate( - minimum: Option, - maximum: Option, - default: T, - lower_inclusive: bool, - upper_inclusive: bool, - step: Option, - value: T, -) -> bool -where - T: SimpleRangeItem, -{ - - if let (Some(step), Some(min)) = (step, minimum) { - let prepared_value: T = value - min; - // We can check the step if we subtract the value from the minimum value - // then see if the remainder of prepared value and step is zero. - // e.g. 4, 12, value is 7, step is 3 - // 7 - 4 = 3 - // 3 % 3 = 0 Valid! - if prepared_value % step != T::ZERO { - return false - } - } - - if value == default { - return true - } - - if let Some(min) = minimum { - let test = if lower_inclusive { - min <= value - } else { - min < value - }; - if test { - return false - } - } - - if let Some(max) = maximum { - let test = if upper_inclusive { - max >= value - } else { - max > value - }; - if test { - return false - } - } - - true -} - -pub trait SimpleRangeItem: Copy + Clone + Debug + Div + Sub + Rem + PartialOrd + PartialEq { +pub trait RangeItem: Copy + Clone + Debug + Div + Sub + Rem + Hash + Ord + PartialOrd + Eq + PartialEq { const ZERO: Self; } macro_rules! impl_num { ($($n:ty)*) => ($( - impl SimpleRangeItem for $n { + impl RangeItem for $n { const ZERO: $n = 0; } )*) @@ -444,10 +192,10 @@ macro_rules! impl_num { impl_num! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 } -impl SimpleRangeItem for f32 { - const ZERO: Self = 0_f32; +impl RangeItem for OrderedFloat { + const ZERO: Self = OrderedFloat(0_f32); } -impl SimpleRangeItem for f64 { - const ZERO: Self = 0_f64; +impl RangeItem for OrderedFloat { + const ZERO: Self = OrderedFloat(0_f64); } \ No newline at end of file diff --git a/nokhwa-core/src/stream.rs b/nokhwa-core/src/stream.rs index 579e557..143d67b 100644 --- a/nokhwa-core/src/stream.rs +++ b/nokhwa-core/src/stream.rs @@ -1,9 +1,17 @@ use crate::error::{NokhwaError, NokhwaResult}; use crate::frame_buffer::FrameBuffer; -use flume::{Receiver, TryRecvError}; use std::sync::Arc; +use derive_builder::Builder; +use flume::{Receiver, TryRecvError}; + +#[derive(Clone, Debug, Default, PartialEq, Builder)] +pub struct StreamConfiguration { + buffer_size: Option, + +} pub trait StreamInnerTrait { + fn configuration(&self) -> &StreamConfiguration; fn receiver(&self) -> Arc>; fn stop(&mut self) -> NokhwaResult<()>; } @@ -19,13 +27,6 @@ impl Stream { } } - // pub unsafe fn erase_lifetime(self) -> Stream<'static> { - // Self { - // inner: self.inner, - // phantom_data: Default::default(), - // } - // } - pub fn check_disconnected(&self) -> NokhwaResult<()> { if self.inner.receiver().is_disconnected() { return Err(NokhwaError::ReadFrameError( diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index c732065..21db24d 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -11,8 +11,8 @@ use std::{ }; use std::num::NonZeroI32; use std::ops::{Div, Rem}; -use num_rational::Rational32; -use crate::ranges::{SimpleRangeItem}; +use num_rational::{Ratio, Rational32}; +use crate::ranges::{RangeItem}; use num_traits::FromPrimitive; /// Describes the index of the camera. @@ -106,6 +106,7 @@ pub struct Resolution { impl Resolution { /// Create a new resolution from 2 image size coordinates. #[must_use] + // TODO: make this height and width. pub const fn new(x: u32, y: u32) -> Self { Resolution { width_x: x, @@ -211,7 +212,7 @@ impl Rem for Resolution { } } -impl SimpleRangeItem for Resolution { +impl RangeItem for Resolution { const ZERO: Self = Resolution::new(0, 0); } @@ -305,7 +306,7 @@ impl Rem for FrameRate { } } -impl SimpleRangeItem for FrameRate { +impl RangeItem for FrameRate { const ZERO: Self = FrameRate::frame_rate(0); } diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index 8a3a067..1cd0260 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -34,7 +34,7 @@ use nokhwa_core::{ use std::{ffi::CString, sync::Arc}; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; /// The backend struct that interfaces with V4L2. /// To see what this does, please see [`CaptureTrait`]. diff --git a/src/backends/capture/browser_camera.rs b/src/backends/capture/browser_camera.rs index eefa08d..71436e7 100644 --- a/src/backends/capture/browser_camera.rs +++ b/src/backends/capture/browser_camera.rs @@ -7,7 +7,7 @@ use serde::{de, Serialize}; use wasm_bindgen_futures::JsFuture; use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator}; use nokhwa_core::frame_buffer::FrameBuffer; -use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; use nokhwa_core::error::NokhwaError; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait}; diff --git a/src/backends/capture/msmf_backend.rs b/src/backends/capture/msmf_backend.rs index 14f876d..d792a49 100644 --- a/src/backends/capture/msmf_backend.rs +++ b/src/backends/capture/msmf_backend.rs @@ -26,7 +26,7 @@ use nokhwa_core::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{all_known_camera_controls, CameraControl, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{all_known_camera_controls, CameraControl, ControlValue, KnownCameraControl}; /// The backend that deals with Media Foundation on Windows. /// To see what this does, please see [`CaptureTrait`]. diff --git a/src/backends/capture/opencv_backend.rs b/src/backends/capture/opencv_backend.rs index 9835847..b4d60b2 100644 --- a/src/backends/capture/opencv_backend.rs +++ b/src/backends/capture/opencv_backend.rs @@ -33,7 +33,7 @@ use opencv::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; /// Attempts to convert a [`KnownCameraControl`] into a `OpenCV` video capture property. /// If the associated control is not found, this will return `Err` diff --git a/src/backends/capture/v4l2_backend.rs b/src/backends/capture/v4l2_backend.rs index 51c2b5b..26a0fb0 100644 --- a/src/backends/capture/v4l2_backend.rs +++ b/src/backends/capture/v4l2_backend.rs @@ -16,7 +16,7 @@ use nokhwa_core::{ camera::{Open, Setting}, error::{NokhwaError, NokhwaResult}, frame_format::FrameFormat, - properties::CameraProperties, + control::CameraProperties, types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution} }; diff --git a/src/camera.rs b/src/camera.rs index 7c12667..170079a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -28,7 +28,7 @@ use nokhwa_core::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; /// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use. pub struct Camera { diff --git a/src/threaded.rs b/src/threaded.rs index b599a7d..03fc082 100644 --- a/src/threaded.rs +++ b/src/threaded.rs @@ -31,7 +31,7 @@ use std::{ Arc, Mutex, }, }; -use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; +use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; type AtomicLock = Arc>; pub type CallbackFn = fn( @@ -420,7 +420,7 @@ impl CallbackCamera { /// Sets the control to `control` in the camera. /// Usually, the pipeline is calling [`camera_control()`](crate::camera_traits::CaptureTrait::camera_control), getting a camera control that way - /// then calling [`value()`](nokhwa_core::properties::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::properties::ControlValue) and setting the value that way. + /// then calling [`value()`](nokhwa_core::control::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::control::ControlValue) and setting the value that way. /// # Errors /// If the `control` is not supported, the value is invalid (less than min, greater than max, not in step), or there was an error setting the control, /// this will error.