From 13633ae5687a93f9d99aabcdcb8713e3f615d92c Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Sun, 18 Aug 2024 22:34:05 +0900 Subject: [PATCH] preliminary browsercamera commit --- Cargo.lock | 693 ++++-- Cargo.toml | 10 +- nokhwa-bindings-linux/src/lib.rs | 12 +- nokhwa-bindings-macos/Cargo.toml | 4 +- nokhwa-core/Cargo.toml | 13 +- nokhwa-core/src/buffer.rs | 154 +- nokhwa-core/src/decoder.rs | 74 - nokhwa-core/src/decoders/general.rs | 58 + nokhwa-core/src/decoders/mod.rs | 67 + nokhwa-core/src/error.rs | 2 + nokhwa-core/src/format_request.rs | 252 +-- nokhwa-core/src/frame_format.rs | 25 +- nokhwa-core/src/lib.rs | 5 +- nokhwa-core/src/traits.rs | 55 +- nokhwa-core/src/types.rs | 277 +-- nokhwa-core/src/utils.rs | 2 +- src/backends/capture/browser_camera.rs | 1102 +++------- src/decoders/mjpeg.rs | 164 +- src/decoders/nv12.rs | 82 +- src/decoders/yuyv.rs | 108 +- src/js_camera.rs | 2720 ------------------------ 21 files changed, 1409 insertions(+), 4470 deletions(-) delete mode 100644 nokhwa-core/src/decoder.rs create mode 100644 nokhwa-core/src/decoders/general.rs create mode 100644 nokhwa-core/src/decoders/mod.rs delete mode 100644 src/js_camera.rs diff --git a/Cargo.lock b/Cargo.lock index 12088bf..8f05a63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,6 +153,15 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading 0.8.3", +] + [[package]] name = "async-trait" version = "0.1.79" @@ -170,6 +179,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "av-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84c5aabf29c696755dca7e0c562717fd958a7dd99befcc9daf0f5f55a78868c" +dependencies = [ + "core-foundation 0.9.4", + "core-media", + "core-video", + "dispatch2", + "libc", + "objc2", + "objc2-foundation", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -214,7 +238,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" +dependencies = [ + "bit-vec 0.7.0", ] [[package]] @@ -223,6 +256,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" + [[package]] name = "bit_field" version = "0.10.2" @@ -237,9 +276,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block" @@ -247,6 +286,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.15.4" @@ -373,6 +421,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "clang" version = "2.0.0" @@ -584,6 +641,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "core-audio-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227f9f306a2bdc12f249ca8f1142da345087acea11dc8410924fc7412962938b" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys 0.8.6", + "libc", +] + [[package]] name = "core-foundation" version = "0.6.4" @@ -681,6 +749,35 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics2" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df90501c70eaf5fdfa6f87516be7a5be894c70569ca1c141f1153e957932c879" +dependencies = [ + "bitflags 2.6.0", + "block", + "cfg-if 1.0.0", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-media" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9732f759501b2c4eaa3bb17d7c8bf72f4bd8a85d5ba9951d12be0cb22fbc1a0" +dependencies = [ + "block", + "cfg-if 1.0.0", + "core-audio-types", + "core-foundation 0.9.4", + "core-graphics2", + "core-video", + "libc", + "objc2", +] + [[package]] name = "core-media-sys" version = "0.1.2" @@ -704,6 +801,20 @@ dependencies = [ "libc", ] +[[package]] +name = "core-video" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba059c242e55689993e100fdcc439d76bffc5510060e68309d2926c34c70cbd0" +dependencies = [ + "block", + "core-foundation 0.9.4", + "core-graphics2", + "io-surface", + "libc", + "objc2", +] + [[package]] name = "core-video-sys" version = "0.1.4" @@ -853,11 +964,22 @@ dependencies = [ [[package]] name = "d3d12" -version = "0.19.0" +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 = "d3d12" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" +checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libloading 0.8.3", "winapi", ] @@ -915,17 +1037,6 @@ dependencies = [ "nokhwa-core", ] -[[package]] -name = "derive_destructure2" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - [[package]] name = "directories" version = "4.0.1" @@ -952,6 +1063,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de2a06334da574f293150949a2bb7c4916c76b0c3a4e8ffd20ec4d71c2be9a5" +dependencies = [ + "libc", +] + [[package]] name = "dlib" version = "0.5.2" @@ -961,6 +1081,15 @@ dependencies = [ "libloading 0.8.3", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "downcast-rs" version = "1.2.0" @@ -1405,6 +1534,15 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +dependencies = [ + "gl_generator", +] + [[package]] name = "glyph_brush" version = "0.7.8" @@ -1459,7 +1597,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "gpu-alloc-types 0.3.0", ] @@ -1478,7 +1616,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1494,14 +1632,38 @@ dependencies = [ "windows 0.52.0", ] +[[package]] +name = "gpu-allocator" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" +dependencies = [ + "log", + "presser", + "thiserror", + "winapi", + "windows 0.52.0", +] + [[package]] name = "gpu-descriptor" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.5.0", - "gpu-descriptor-types", + "bitflags 2.6.0", + "gpu-descriptor-types 0.1.2", + "hashbrown 0.14.3", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" +dependencies = [ + "bitflags 2.6.0", + "gpu-descriptor-types 0.2.0", "hashbrown 0.14.3", ] @@ -1511,7 +1673,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.6.0", ] [[package]] @@ -1546,7 +1717,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "com", "libc", "libloading 0.8.3", @@ -1673,6 +1844,18 @@ dependencies = [ "mach 0.2.3", ] +[[package]] +name = "io-surface" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "861c6093cbc05599e66436aedf380bb0a23cec2180738393d3a340b80dd135ef" +dependencies = [ + "cgl", + "core-foundation 0.9.4", + "leaky-cow", + "libc", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1694,20 +1877,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jobslot" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0303fc691930667d6fd0b2cd194f9d6b37be3d890e0b89cf4a34e27231f35c4f" -dependencies = [ - "cfg-if 1.0.0", - "derive_destructure2", - "getrandom", - "libc", - "scopeguard", - "windows-sys 0.52.0", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -1766,6 +1935,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" + +[[package]] +name = "leaky-cow" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" +dependencies = [ + "leak", +] + [[package]] name = "lebe" version = "0.5.2" @@ -1795,7 +1979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1810,7 +1994,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", "redox_syscall", ] @@ -1837,6 +2021,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.11" @@ -1996,11 +2186,26 @@ dependencies = [ [[package]] name = "metal" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.6.0", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -2073,7 +2278,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bb1a241901d94de4725d638a5295187b046ceb89ed8b2be368038654d9c81d" dependencies = [ - "bit-set", + "bit-set 0.5.3", "bitflags 1.3.2", "codespan-reporting", "hexf-parse", @@ -2089,12 +2294,13 @@ dependencies = [ [[package]] name = "naga" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" dependencies = [ - "bit-set", - "bitflags 2.5.0", + "arrayvec 0.7.4", + "bit-set 0.5.3", + "bitflags 2.6.0", "codespan-reporting", "hexf-parse", "indexmap 2.2.6", @@ -2107,6 +2313,27 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "naga" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09eeccb9b50f4f7839b214aa3e08be467159506a986c18e0702170ccf720a453" +dependencies = [ + "arrayvec 0.7.4", + "bit-set 0.6.0", + "bitflags 2.6.0", + "cfg_aliases", + "codespan-reporting", + "hexf-parse", + "indexmap 2.2.6", + "log", + "rustc-hash", + "spirv 0.3.0+sdk-1.3.268.0", + "termcolor", + "thiserror", + "unicode-xid", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -2236,13 +2463,14 @@ dependencies = [ "regex", "rgb", "serde", + "serde-wasm-bindgen", "thiserror", "usb_enumeration", "wasm-bindgen", "wasm-bindgen-futures", "wasm-rs-async-executor", "web-sys", - "wgpu 0.19.3", + "wgpu 0.20.0", ] [[package]] @@ -2258,11 +2486,12 @@ dependencies = [ name = "nokhwa-bindings-macos" version = "0.2.0" dependencies = [ + "av-foundation", "block", "cocoa-foundation", "core-media-sys", "core-video-sys", - "flume 0.10.14", + "flume 0.11.0", "nokhwa-core", "objc", "once_cell", @@ -2283,13 +2512,15 @@ version = "0.2.0" dependencies = [ "async-trait", "bytes", + "dcv-color-primitives", "image 0.25.0", "mozjpeg", "opencv", "paste", "serde", "thiserror", - "wgpu 0.19.3", + "wgpu 22.0.0", + "yuvutils-rs", ] [[package]] @@ -2387,6 +2618,40 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2413,13 +2678,13 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opencv" -version = "0.89.0" +version = "0.92.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38147846b39e60b686bf8f5bd289b0a667c5bb629e37c88569339a07dab97970" +checksum = "5e19ccfa4237826b2213bda6dd30875b9ed184c5318ec64d3c269132d128212c" dependencies = [ "cc", "dunce", - "jobslot", + "jobserver", "libc", "num-traits", "once_cell", @@ -2429,13 +2694,14 @@ dependencies = [ "semver", "shlex", "vcpkg", + "windows 0.56.0", ] [[package]] name = "opencv-binding-generator" -version = "0.85.0" +version = "0.90.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f742feaf7cbc1279aea73d7bf0834896ca5f226cc9230d212e5092440776b81c" +checksum = "86ddd32f38d5cd32c79a38da363528ced3149a48f1e686b7a12913a184a84cd5" dependencies = [ "clang", "clang-sys", @@ -2629,7 +2895,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "memchr", "unicase", ] @@ -2817,7 +3083,7 @@ version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2890,6 +3156,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -3056,7 +3333,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -3122,18 +3399,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -3589,16 +3866,17 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.19.3" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1213b52478a7631d6e387543ed8f642bc02c578ef4e3b49aca2a29a7df0cb" +checksum = "32ff1bfee408e1028e2e3acbf6d32d98b08a5a059ccbf5f33305534453ba5d3e" dependencies = [ "arrayvec 0.7.4", "cfg-if 1.0.0", "cfg_aliases", + "document-features", "js-sys", "log", - "naga 0.19.2", + "naga 0.20.0", "parking_lot", "profiling", "raw-window-handle 0.6.0", @@ -3607,9 +3885,34 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu-core 0.19.3", - "wgpu-hal 0.19.3", - "wgpu-types 0.19.2", + "wgpu-core 0.20.0", + "wgpu-hal 0.20.0", + "wgpu-types 0.20.0", +] + +[[package]] +name = "wgpu" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87e07e87a179614940ad845397e03201847453a37b43a31a3b54eee2e6e32ce" +dependencies = [ + "arrayvec 0.7.4", + "cfg_aliases", + "document-features", + "js-sys", + "log", + "naga 22.0.0", + "parking_lot", + "profiling", + "raw-window-handle 0.6.0", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core 22.0.0", + "wgpu-hal 22.0.0", + "wgpu-types 22.0.0", ] [[package]] @@ -3619,7 +3922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6000d1284ef8eec6076fd5544a73125fd7eb9b635f18dceeb829d826f41724ca" dependencies = [ "arrayvec 0.7.4", - "bit-vec", + "bit-vec 0.6.3", "bitflags 1.3.2", "cfg_aliases", "codespan-reporting", @@ -3638,18 +3941,19 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.19.3" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f6b033c2f00ae0bc8ea872c5989777c60bc241aac4e58b24774faa8b391f78" +checksum = "ac6a86eaa5e763e59c73cf9e97d55fffd4dfda69fd8bda19589fcf851ddfef1f" dependencies = [ "arrayvec 0.7.4", - "bit-vec", - "bitflags 2.5.0", + "bit-vec 0.6.3", + "bitflags 2.6.0", "cfg_aliases", "codespan-reporting", + "document-features", "indexmap 2.2.6", "log", - "naga 0.19.2", + "naga 0.20.0", "once_cell", "parking_lot", "profiling", @@ -3658,8 +3962,33 @@ dependencies = [ "smallvec", "thiserror", "web-sys", - "wgpu-hal 0.19.3", - "wgpu-types 0.19.2", + "wgpu-hal 0.20.0", + "wgpu-types 0.20.0", +] + +[[package]] +name = "wgpu-core" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f191908a21968991463fcf3b42cb6c9648c0fb7fa301b8fc733bc21a9ed9bd" +dependencies = [ + "arrayvec 0.7.4", + "bit-vec 0.7.0", + "bitflags 2.6.0", + "cfg_aliases", + "document-features", + "indexmap 2.2.6", + "log", + "naga 22.0.0", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle 0.6.0", + "rustc-hash", + "smallvec", + "thiserror", + "wgpu-hal 22.0.0", + "wgpu-types 22.0.0", ] [[package]] @@ -3670,8 +3999,8 @@ checksum = "3cc320a61acb26be4f549c9b1b53405c10a223fbfea363ec39474c32c348d12f" dependencies = [ "android_system_properties", "arrayvec 0.7.4", - "ash", - "bit-set", + "ash 0.37.3+1.3.251", + "bit-set 0.5.3", "bitflags 1.3.2", "block", "core-graphics-types", @@ -3680,7 +4009,7 @@ dependencies = [ "fxhash", "glow 0.11.2", "gpu-alloc 0.5.4", - "gpu-descriptor", + "gpu-descriptor 0.2.4", "js-sys", "khronos-egl 4.1.0", "libloading 0.7.4", @@ -3703,32 +4032,32 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.19.3" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f972c280505ab52ffe17e94a7413d9d54b58af0114ab226b9fc4999a47082e" +checksum = "4d71c8ae05170583049b65ee562fd839fdc0b3e9ddb84f4e40c9d5f8ea0d4c8c" dependencies = [ "android_system_properties", "arrayvec 0.7.4", - "ash", - "bit-set", - "bitflags 2.5.0", + "ash 0.37.3+1.3.251", + "bit-set 0.5.3", + "bitflags 2.6.0", "block", "cfg_aliases", "core-graphics-types", - "d3d12 0.19.0", + "d3d12 0.20.0", "glow 0.13.1", - "glutin_wgl_sys", + "glutin_wgl_sys 0.5.0", "gpu-alloc 0.6.0", - "gpu-allocator", - "gpu-descriptor", + "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.27.0", - "naga 0.19.2", + "metal 0.28.0", + "naga 0.20.0", "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", @@ -3742,7 +4071,52 @@ dependencies = [ "thiserror", "wasm-bindgen", "web-sys", - "wgpu-types 0.19.2", + "wgpu-types 0.20.0", + "winapi", +] + +[[package]] +name = "wgpu-hal" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" +dependencies = [ + "android_system_properties", + "arrayvec 0.7.4", + "ash 0.38.0+1.3.281", + "bit-set 0.6.0", + "bitflags 2.6.0", + "block", + "cfg_aliases", + "core-graphics-types", + "d3d12 22.0.0", + "glow 0.13.1", + "glutin_wgl_sys 0.6.0", + "gpu-alloc 0.6.0", + "gpu-allocator 0.26.0", + "gpu-descriptor 0.3.0", + "hassle-rs", + "js-sys", + "khronos-egl 6.0.0", + "libc", + "libloading 0.8.3", + "log", + "metal 0.29.0", + "naga 22.0.0", + "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", + "wasm-bindgen", + "web-sys", + "wgpu-types 22.0.0", "winapi", ] @@ -3757,11 +4131,22 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.19.2" +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 = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "js-sys", "web-sys", ] @@ -3836,8 +4221,18 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", - "windows-targets 0.52.4", + "windows-core 0.52.0", + "windows-targets 0.52.5", +] + +[[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.5", ] [[package]] @@ -3846,7 +4241,50 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.5", +] + +[[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.55", +] + +[[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.55", +] + +[[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.5", ] [[package]] @@ -3877,7 +4315,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -3897,17 +4335,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -3924,9 +4363,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -3948,9 +4387,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -3972,9 +4411,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -3996,9 +4441,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4020,9 +4465,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4038,9 +4483,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4062,9 +4507,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winit" @@ -4147,6 +4592,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +[[package]] +name = "yuvutils-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b579307cc265a693bc908bf2dbde8578ba7e7fe1a7dcc71dbd6b84e063f356" + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 63e5060..b7f8ab3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ input-native = ["input-avfoundation", "input-v4l", "input-msmf"] # Re-enable it once soundness has been proven + mozjpeg is updated to 0.9.x # input-uvc = ["uvc", "uvc/vendor", "usb_enumeration", "lazy_static"] input-opencv = ["opencv", "opencv/rgb", "rgb", "nokhwa-core/opencv-mat"] -input-jscam = [ "wasm-bindgen-futures", "wasm-rs-async-executor", "output-async", "js-sys", "web-sys"] +input-jscam = [ "wasm-bindgen-futures", "wasm-rs-async-executor", "output-async", "js-sys", "web-sys", "serde-wasm-bindgen", "serde"] output-wgpu = ["wgpu", "nokhwa-core/wgpu-types"] #output-wasm = ["input-jscam"] output-threaded = [] @@ -73,11 +73,11 @@ version = "0.2" optional = true [dependencies.wgpu] -version = "0.19" +version = "0.20" optional = true [dependencies.opencv] -version = "0.89" +version = "0.92" default-features = false features = ["videoio"] optional = true @@ -143,6 +143,10 @@ optional = true version = "0.9" optional = true +[dependencies.serde-wasm-bindgen] +version = "0.6" +optional = true + [dependencies.async-trait] version = "0.1" optional = true diff --git a/nokhwa-bindings-linux/src/lib.rs b/nokhwa-bindings-linux/src/lib.rs index f879f23..8344efa 100644 --- a/nokhwa-bindings-linux/src/lib.rs +++ b/nokhwa-bindings-linux/src/lib.rs @@ -178,11 +178,11 @@ mod internal { } }; - self.camera_format = CameraFormat::new( + self.camera_format = Some(CameraFormat::new( Resolution::new(format.width, format.height), frame_format: , fps, - ); + )); Ok(()) } Err(why) => Err(NokhwaError::GetPropertyError { @@ -198,10 +198,6 @@ mod internal { todo!() } - fn init_with_format(&mut self, format: FormatFilter) -> Result { - todo!() - } - fn backend(&self) -> ApiBackend { ApiBackend::Video4Linux } @@ -596,8 +592,8 @@ mod internal { fn fourcc_to_frameformat(fourcc: FourCC) -> Option { match fourcc.str().ok()? { - "YUYV" => Some(FrameFormat::Yuv422), - "UYVY" => Some(FrameFormat::Uyv422), + "YUYV" => Some(FrameFormat::Yuy2_422), + "UYVY" => Some(FrameFormat::Uyvy_422), "YV12" => Some(FrameFormat::Yv12), "MJPG" => Some(FrameFormat::MJpeg), "GRAY" => Some(FrameFormat::Luma8), diff --git a/nokhwa-bindings-macos/Cargo.toml b/nokhwa-bindings-macos/Cargo.toml index c702ad6..914116b 100644 --- a/nokhwa-bindings-macos/Cargo.toml +++ b/nokhwa-bindings-macos/Cargo.toml @@ -21,5 +21,7 @@ core-video-sys = "0.1" cocoa-foundation = "0.1" objc = { version = "0.2", features = ["exception"] } block = "0.1" -flume = "0.10" +flume = "0.11.0" once_cell = "1.16" + +av-foundation = "0.3.0" \ No newline at end of file diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index 1875673..33038ca 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -16,6 +16,7 @@ serialize = ["serde"] wgpu-types = ["wgpu"] opencv-mat = ["opencv"] docs-features = ["serialize", "wgpu-types"] +conversions = ["dcv-color-primitives", "yuvutils-rs", "mozjpeg"] async = ["async-trait"] test-fail-warnings = [] @@ -35,11 +36,11 @@ features = ["derive"] optional = true [dependencies.wgpu] -version = "0.19" +version = "22" optional = true [dependencies.opencv] -version = "0.89.0" +version = "0.92" default-features = false optional = true @@ -51,5 +52,13 @@ optional = true version = "0.1" optional = true +[dependencies.dcv-color-primitives] +version = "0.6" +optional = true + +[dependencies.yuvutils-rs] +version = "0.3" +optional = true + [package.metadata.docs.rs] features = ["docs-features"] diff --git a/nokhwa-core/src/buffer.rs b/nokhwa-core/src/buffer.rs index ea9a966..2049bb8 100644 --- a/nokhwa-core/src/buffer.rs +++ b/nokhwa-core/src/buffer.rs @@ -14,8 +14,9 @@ * limitations under the License. */ -use crate::{ types::Resolution}; +use crate::types::Resolution; use bytes::Bytes; +use crate::frame_format::FrameFormat; /// A buffer returned by a camera to accommodate custom decoding. /// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer. @@ -64,154 +65,3 @@ impl Buffer { self.source_frame_format } } - -#[cfg(feature = "opencv-mat")] -use crate::error::NokhwaError; -#[cfg(feature = "opencv-mat")] -use image::ImageBuffer; - -#[cfg(feature = "opencv-mat")] -impl Buffer { - - /// Decodes a image with allocation using the provided [`FormatDecoder`]. - /// # Errors - /// Will error when the decoding fails. - #[inline] - pub fn decode_image( - &self, - ) -> Result>, NokhwaError> { - let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?; - let image = - ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data) - .ok_or(NokhwaError::ProcessFrameError { - src: self.source_frame_format, - destination: stringify!(F).to_string(), - error: "Failed to create buffer".to_string(), - })?; - Ok(image) - } - - /// Decodes a image with allocation using the provided [`FormatDecoder`] into a `buffer`. - /// # Errors - /// Will error when the decoding fails, or the provided buffer is too small. - #[inline] - pub fn decode_image_to_buffer( - &self, - buffer: &mut [u8], - ) -> Result<(), NokhwaError> { - F::write_output_buffer( - self.source_frame_format, - self.resolution, - &self.buffer, - buffer, - ) - } - /// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). - /// - /// Note that this does a clone when creating the buffer, to decouple the lifetime of the internal data to the temporary Buffer. If you want to avoid this, please see [`decode_opencv_mat`](Self::decode_opencv_mat). - /// # Errors - /// Will error when the decoding fails, or `OpenCV` failed to create/copy the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). - /// # Safety - /// This function uses `unsafe` in order to create the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). Please see [`Mat::new_rows_cols_with_data`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html#method.new_rows_cols_with_data) for more. - /// - /// Most notably, the `data` **must** stay in scope for the duration of the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html) or bad, ***bad*** things happen. - #[cfg(feature = "opencv-mat")] - #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))] - #[allow(clippy::cast_possible_wrap)] - pub fn decode_opencv_mat( - &mut self, - ) -> Result { - use image::Pixel; - use opencv::core::{Mat, Mat_AUTO_STEP, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4}; - - let array_type = match F::Output::CHANNEL_COUNT { - 1 => CV_8UC1, - 2 => CV_8UC2, - 3 => CV_8UC3, - 4 => CV_8UC4, - _ => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::RAWRGB, - destination: "OpenCV Mat".to_string(), - error: "Invalid Decoder FormatDecoder Channel Count".to_string(), - }) - } - }; - - unsafe { - // TODO: Look into removing this unnecessary copy. - let mat1 = Mat::new_rows_cols_with_data( - self.resolution.height_y as i32, - self.resolution.width_x as i32, - array_type, - self.buffer.as_ref().as_ptr().cast_mut().cast(), - Mat_AUTO_STEP, - ) - .map_err(|why| NokhwaError::ProcessFrameError { - src: FrameFormat::Rgb8, - destination: "OpenCV Mat".to_string(), - error: why.to_string(), - })?; - - Ok(mat1) - } - } -} - -#[cfg(feature = "wgpu-types")] -use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, ImageCopyTexture, TextureAspect, ImageDataLayout}; -use crate::frame_format::FrameFormat; - -#[cfg(feature = "wgpu-types")] -impl Buffer { - #[cfg_attr(feature = "docs-features", doc(cfg(feature = "wgpu-types")))] - /// Directly copies a frame to a Wgpu texture. This will automatically convert the frame into a RGBA frame. - /// # Errors - /// If the frame cannot be captured or the resolution is 0 on any axis, this will error. - fn frame_texture<'a>( - &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - label: Option<&'a str>, - ) -> Result { - let frame = self.frame()?.decode_image::()?; - - let texture_size = Extent3d { - width: frame.width(), - height: frame.height(), - depth_or_array_layers: 1, - }; - - let texture = device.create_texture(&TextureDescriptor { - label, - size: texture_size, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: TextureFormat::Rgba8UnormSrgb, - usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - view_formats: &[TextureFormat::Rgba8UnormSrgb], - }); - - let width_nonzero = 4 * frame.width(); - let height_nonzero = frame.height(); - - queue.write_texture( - ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: TextureAspect::All, - }, - &frame, - ImageDataLayout { - offset: 0, - bytes_per_row: width_nonzero, - rows_per_image: height_nonzero, - }, - texture_size, - ); - - Ok(texture) - } -} diff --git a/nokhwa-core/src/decoder.rs b/nokhwa-core/src/decoder.rs deleted file mode 100644 index 8b6f990..0000000 --- a/nokhwa-core/src/decoder.rs +++ /dev/null @@ -1,74 +0,0 @@ -use image::{ImageBuffer, Pixel}; -use crate::buffer::Buffer; -use crate::frame_format::FrameFormat; - -/// Trait to define a struct that can decode a [`Buffer`] -pub trait Decoder { - /// Formats that the decoder can decode. - const ALLOWED_FORMATS: &'static FrameFormat; - /// Output pixel type (e.g. [`Rgb`](image::Rgb)) - type OutputPixels: Pixel; - - type PixelContainer; - /// Error that the decoder will output (use [`NokhwaError`] if you're not sure) - type Error; - - /// Decode function. - fn decode(&mut self, buffer: Buffer) -> Result, Self::Error>; - - /// Decode to user-provided Buffer - /// - /// Incase that the buffer is not large enough this should error. - fn decode_buffer(&mut self, buffer: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; - - /// Decoder Predicted Size - fn predicted_size_of_frame(&mut self, ) -> Option; -} - -/// Decoder that can be used statically (struct contains no state) -/// -/// This is useful for times that a simple function is all that is required. -pub trait StaticDecoder: Decoder { - fn decode_static(buffer: Buffer) -> Result, Self::Error>; - - fn decode_static_to_buffer(buffer: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; -} - -/// Decoder that does not change its internal state. -pub trait IdemptDecoder: Decoder { - /// Decoder that does not change its internal state. - fn decode_nm(&self, buffer: Buffer) -> Result, Self::Error>; - - /// Decoder that does not change its internal state, decoding to a user provided buffer. - fn decode_nm_to_buffer(&self, buffer: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; -} - -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncDecoder: Decoder { - /// Asynchronous decoder - async fn decode_async(&mut self, buffer: Buffer) -> Result, Self::Error>; - - /// Asynchronous decoder to user buffer. - async fn decode_buffer(&mut self, buffer: &mut [Self::OutputPixels::Subpixel]) -> Result<(), Self::Error>; -} - -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncStaticDecoder: Decoder { - /// Asynchronous decoder - async fn decode_static_async(buffer: Buffer) -> Result, Self::Error>; - - /// Asynchronous decoder to user buffer. - async fn decode_static_buffer(buffer: &mut [Self::OutputPixels::Subpixel]) -> Result<(), Self::Error>; -} - -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncIdemptDecoder: Decoder { - /// Asynchronous decoder - async fn decode_nm_async(&self, buffer: Buffer) -> Result, Self::Error>; - - /// Asynchronous decoder to user buffer. - async fn decode_nm_buffer(&self, buffer: &mut [Self::OutputPixels::Subpixel]) -> Result<(), Self::Error>; -} diff --git a/nokhwa-core/src/decoders/general.rs b/nokhwa-core/src/decoders/general.rs new file mode 100644 index 0000000..8696526 --- /dev/null +++ b/nokhwa-core/src/decoders/general.rs @@ -0,0 +1,58 @@ +use dcv_color_primitives::PixelFormat; +use image::{ExtendedColorType, ImageBuffer, Pixel, PixelWithColorType}; +use crate::buffer::Buffer; +use crate::decoders::Decoder; +use crate::error::NokhwaError; +use crate::frame_format::FrameFormat; + +pub struct GeneralPurposeDecoder where D: PixelWithColorType; + +impl Decoder for GeneralPurposeDecoder where D: PixelWithColorType { + const ALLOWED_FORMATS: &'static [FrameFormat] = &[ + FrameFormat::MJpeg, FrameFormat::Luma8, FrameFormat::Luma16, FrameFormat::Rgb8, FrameFormat::RgbA8, + FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Uyvy_422, FrameFormat::Yuy2_422, FrameFormat::Yv12, + FrameFormat::Yuv444, FrameFormat::I420, FrameFormat::I422, FrameFormat::I444 + ]; + + type OutputPixels = D; + type PixelContainer = Vec; + type Error = NokhwaError; + + fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { + todo!() + } + + fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error> { + if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) { + return Err(NokhwaError::ConversionError(format!("Invaid frame format {} (allowed formats: {:?})", buffer.source_frame_format(), Self::ALLOWED_FORMATS))) + } + + let destination = match D::COLOR_TYPE { + ExtendedColorType::Rgb8 => PixelFormat::Rgb, + ExtendedColorType::Rgba8 => PixelFormat::Rgba, + ExtendedColorType::Bgr8 => PixelFormat::Bgr, + ExtendedColorType::Bgra8 => PixelFormat::Bgra, + _ => return Err(()) + }; + + // some extra processing needed for some formats + + let source = match buffer.source_frame_format() { + FrameFormat::MJpeg => PixelFormat::Rgb, // => JPEG decoder + FrameFormat::Yuy2_422 => PixelFormat::I422, + FrameFormat::Uyvy_422 => PixelFormat::I422, + FrameFormat::Yuv444 => PixelFormat::I444, + FrameFormat::Nv12 => PixelFormat::Nv12, + FrameFormat::Nv21 => PixelFormat::Nv12, + FrameFormat::Yv12 => PixelFormat::I420, + FrameFormat::I420 => PixelFormat::I420, + // already decoded + FrameFormat::Rgb8 => PixelFormat::Rgb, + FrameFormat::RgbA8 => { + PixelFormat::Rgba + } + _ => return Err(()), + }; + + } +} diff --git a/nokhwa-core/src/decoders/mod.rs b/nokhwa-core/src/decoders/mod.rs new file mode 100644 index 0000000..da9d2d7 --- /dev/null +++ b/nokhwa-core/src/decoders/mod.rs @@ -0,0 +1,67 @@ + +use std::ops::Deref; +use image::{ExtendedColorType, ImageBuffer, Pixel}; +use crate::buffer::Buffer; +use crate::error::NokhwaError; +use crate::frame_format::FrameFormat; + +/// Trait to define a struct that can decode a [`Buffer`] +pub trait Decoder { + /// Formats that the decoder can decode. + const ALLOWED_FORMATS: &'static [FrameFormat]; + /// Output pixel type (e.g. [`Rgb`](image::Rgb)) + type OutputPixels: Pixel; + + type PixelContainer: Deref::OutputPixels as Pixel>::Subpixel]>; + /// Error that the decoder will output (use [`NokhwaError`] if you're not sure) + type Error; + + /// Decode function. + fn decode(&mut self, buffer: &Buffer) -> Result, Self::Error>; + + /// Decode to user-provided Buffer + /// + /// Incase that the buffer is not large enough this should error. + fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; + + /// Decoder Predicted Size + fn predicted_size_of_frame(buffer: &Buffer) -> Option { + if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) { + return None + } + let res = buffer.resolution(); + Some(res.x() as usize * res.y() as usize * core::mem::size_of::<<::OutputPixels as Pixel>::Subpixel>() * <::OutputPixels as Pixel>::CHANNEL_COUNT as usize) + } +} + +/// Decoder that can be used statically (struct contains no state) +/// +/// This is useful for times that a simple function is all that is required. +pub trait StaticDecoder: Decoder { + fn decode_static(buffer: &Buffer) -> Result, Self::Error>; + + fn decode_static_to_buffer(&mut self, buffer: &Buffer, output: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; +} + +#[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] +pub trait AsyncDecoder: Decoder { + /// Asynchronous decoder + async fn decode_async(&mut self, buffer: &Buffer) -> Result, Self::Error>; + + /// Asynchronous decoder to user buffer. + async fn decode_buffer(&mut self, buffer: &Buffer, output: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; +} + +#[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] +pub trait AsyncStaticDecoder: Decoder { + /// Asynchronous decoder + async fn decode_static_async(buffer: &Buffer) -> Result, Self::Error>; + + /// Asynchronous decoder to user buffer. + async fn decode_static_buffer(&mut self, buffer: &Buffer, output: &mut [<::OutputPixels as Pixel>::Subpixel]) -> Result<(), Self::Error>; +} + +#[cfg(feature = "conversions")] +pub mod general; diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index decc5a6..aacd93c 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -57,4 +57,6 @@ pub enum NokhwaError { UnsupportedOperationError(ApiBackend), #[error("This operation is not implemented yet: {0}")] NotImplementedError(String), + #[error("Failed To Convert: {0}")] + ConversionError(String), } diff --git a/nokhwa-core/src/format_request.rs b/nokhwa-core/src/format_request.rs index bb209e9..879af66 100644 --- a/nokhwa-core/src/format_request.rs +++ b/nokhwa-core/src/format_request.rs @@ -21,196 +21,90 @@ pub enum CustomFormatRequestType { HighestFrameRate, HighestResolution, Closest, + Exact, } -#[derive(Clone, Debug, Default, PartialOrd, PartialEq)] -pub struct FormatRequest { - resolution: Option>, - frame_rate: Option>, - frame_format: Option>, - req_type: Option, +pub enum FormatRequest { + Closest { + resolution: Option>, + frame_rate: Option>, + frame_format: Vec, + }, + HighestFrameRate { + frame_rate: Range, + frame_format: Vec, + }, + HighestResolution { + resolution: Range, + frame_format: Vec, + }, + Exact { + resolution: Resolution, + frame_rate: FrameRate, + frame_format: Vec, + }, } impl FormatRequest { - pub fn new() -> Self { - Self::default() - } - - pub fn with_standard_frame_formats(mut self) -> Self { - self.append_frame_formats(&mut vec![ - FrameFormat::MJpeg, - FrameFormat::Rgb8, - FrameFormat::Yuv422, - FrameFormat::Nv12, - ]) - } - - pub fn push_frame_format(mut self, frame_format: FrameFormat) -> Self { - match &mut self.frame_format { - Some(ffs) => ffs.push(frame_format), - None => self.frame_format = Some(vec![frame_format]), + #[must_use] + pub fn resolve(&self, list_of_formats: &[CameraFormat]) -> Option { + if list_of_formats.is_empty() { + return None; } - self - } - - pub fn remove_frame_format(mut self, frame_format: FrameFormat) -> Self { - if let Some(ffs) = &mut self.frame_format { - if let Some(idx) = ffs.iter().position(|ff| ff == &frame_format) { - ffs.remove(idx); + match self { + FormatRequest::Closest { resolution, frame_rate, frame_format } => { + let resolution_point = resolution.map(|x| x.preferred())?; + + let frame_rate_point = frame_rate.map(|x| x.preferred())?; + // lets calcuate distance in 3 dimensions (add both resolution and frame_rate together) + + let mut distances = list_of_formats.iter() + .filter(|x| { + frame_format.contains(&x.format()) + }) + .map(|fmt| { + ((fmt.frame_rate() - frame_rate_point).abs() + fmt.resolution().distance_from(&resolution_point) as f32, fmt) + }) + .collect::>(); + distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + VecDeque::from(distances).pop_front().map(|x| x.1).copied() } - } - - self - } - - pub fn append_frame_formats(mut self, frame_formats: &mut Vec) -> Self { - match &mut self.frame_format { - Some(ffs) => ffs.append(frame_formats), - None => self.frame_format = Some(frame_formats.clone()), - } - - self - } - - pub fn reset_frame_formats(mut self) -> Self { - self.frame_format = None; - self - } - - pub fn with_request_type(mut self, request_type: CustomFormatRequestType) -> Self { - self.req_type = Some(request_type); - self - } - - pub fn reset_request_type(mut self) -> Self { - self.req_type = None; - self - } - - pub fn set_resolution_range(mut self, resolution_range: Range) -> Self { - self.resolution = Some(resolution_range); - self - } - - pub fn reset_resolution_range(mut self) -> Self { - self.resolution = None; - self - } - - pub fn set_frame_rate_range(mut self, frame_rate_range: Range) -> Self { - self.frame_rate = Some(frame_rate_range); - self - } - - pub fn reset_frame_rate_range(mut self) -> Self { - self.frame_rate = None; - self - } - - pub fn satisfied_by_format(&self, format: &CameraFormat) -> bool { - // check resolution - let resolution_satisfied = match self.resolution { - Some(res_range) => res_range.in_range(format.resolution()), - None => true, - }; - - let frame_rate_satisfied = match self.frame_rate { - Some(fps_range) => fps_range.in_range(format.frame_rate()), - None => true, - }; - - let frame_format_satisfied = match &self.frame_format { - Some(frame_formats) => frame_formats.contains(&format.format()), - None => true, - }; - - // we ignore custom bc that only makes sense in multiple formats - - resolution_satisfied && frame_rate_satisfied && frame_format_satisfied - } - - - pub fn resolve(&self, list_of_formats: &[CameraFormat]) -> Option { - // filter out bad results - let mut remaining_formats = list_of_formats.iter().filter(|x| self.satisfied_by_format(*x)).copied().collect::>(); - - match self.req_type { - Some(request) => { - match request { - CustomFormatRequestType::HighestFrameRate => { - remaining_formats.sort_by(|a, b| { - a.frame_rate().partial_cmp(&b.frame_rate()).unwrap_or(Ordering::Equal) - }); - Some(remaining_formats[0]) - } - CustomFormatRequestType::HighestResolution => { - remaining_formats.sort_by(|a, b| { - a.frame_rate().partial_cmp(&b.frame_rate()).unwrap_or(Ordering::Equal) - }); - Some(remaining_formats[0]) + FormatRequest::HighestFrameRate { frame_rate, frame_format } => { + let mut formats = list_of_formats.iter().filter(|x| { + frame_format.contains(&x.format()) && frame_rate.in_range(x.frame_rate()) + }).collect::>(); + formats.sort_by(|a, b| { + match a.frame_rate().partial_cmp(&b.frame_rate()) { + None | Some(Ordering::Equal) => a.resolution().cmp(&b.resolution()), + Some(ord) => ord, } - CustomFormatRequestType::Closest => { - - let mut closest_type = match (&self.frame_rate, &self.resolution) { - (Some(_), Some(_)) => ClosestType::Both, - (Some(_), None) => ClosestType::FrameRate, - (None, Some(_)) => ClosestType::Resolution, - (None, None) => ClosestType::None, - }; - - match closest_type { - ClosestType::Resolution => { - let resolution_point = match self.resolution.map(|x| x.preferred()).flatten() { - Some(r) => r, - None => return None, - }; - - let mut distances = remaining_formats.into_iter().map(|fmt| (fmt.resolution().distance_from(&resolution_point), fmt)).collect::>(); - distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or_else(|| Ordering::Equal)); - VecDeque::from(distances).pop_front().map(|x| x.1) - } - - ClosestType::FrameRate => { - let frame_rate_point = match self.frame_rate.map(|x| x.preferred()).flatten() { - Some(f) => f, - None => return None, - }; - - let mut distances = remaining_formats.into_iter().map(|fmt| (fmt.frame_rate().distance_from(&frame_rate_point), fmt)).collect::>(); - distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or_else(|| Ordering::Equal)); - VecDeque::from(distances).pop_front().map(|x| x.1) - } - ClosestType::Both => { - let resolution_point = match self.resolution.map(|x| x.preferred()).flatten() { - Some(r) => r, - None => return None, - }; - - let frame_rate_point = match self.frame_rate.map(|x| x.preferred()).flatten() { - Some(f) => f, - None => return None, - }; - - // lets calcuate distance in 3 dimensions (add both resolution and frame_rate together) - - let mut distances = remaining_formats.into_iter() - .map(|fmt| { - (fmt.frame_rate().distance_from(&frame_rate_point) + fmt.resolution().distance_from(&resolution_point) as f32, fmt) - }) - .collect::>(); - distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or_else(|| Ordering::Equal)); - VecDeque::from(distances).pop_front().map(|x| x.1) - } - ClosestType::None => { - Some(remaining_formats[0]) - } - } + }); + formats.first().copied().copied() + } + FormatRequest::HighestResolution { resolution, frame_format } => { + let mut formats = list_of_formats.iter().filter(|x| { + frame_format.contains(&x.format()) && resolution.in_range(x.resolution()) + }).collect::>(); + formats.sort_by(|a, b| { + match a.resolution().partial_cmp(&b.resolution()) { + None | Some(Ordering::Equal) => a.frame_rate().partial_cmp(&b.frame_rate()).unwrap_or(Ordering::Equal), + Some(ord) => ord, } - } + }); + formats.first().copied().copied() } - None => { - Some(remaining_formats[0]) + FormatRequest::Exact { resolution, frame_rate, frame_format } => { + let mut formats = list_of_formats.iter().filter(|x| { + frame_format.contains(&x.format()) && resolution == &x.resolution() && frame_rate == &x.frame_rate() + }).collect::>(); + formats.sort_by(|a, b| { + match a.resolution().partial_cmp(&b.resolution()) { + None | Some(Ordering::Equal) => a.frame_rate().partial_cmp(&b.frame_rate()).unwrap_or(Ordering::Equal), + Some(ord) => ord, + } + }); + formats.first().copied().copied() } } } diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 13e87a8..557299e 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -36,15 +36,20 @@ pub enum FrameFormat { VP9, // YCbCr formats + + Yuv444, // -> 422 16 BPP - Yuv422, - Uyv422, + Yuy2_422, + Uyvy_422, // 420 Nv12, Nv21, Yv12, + I420, + I422, + I444, // Grayscale Formats Luma8, @@ -72,8 +77,8 @@ impl FrameFormat { FrameFormat::XVid, FrameFormat::VP8, FrameFormat::VP9, - FrameFormat::Yuv422, - FrameFormat::Uyv422, + FrameFormat::Yuy2_422, + FrameFormat::Uyvy_422, FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Yv12, @@ -98,8 +103,8 @@ impl FrameFormat { ]; pub const CHROMA: &'static [FrameFormat] = &[ - FrameFormat::Yuv422, - FrameFormat::Uyv422, + FrameFormat::Yuy2_422, + FrameFormat::Uyvy_422, FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Yv12, @@ -121,8 +126,8 @@ impl FrameFormat { FrameFormat::XVid, FrameFormat::VP8, FrameFormat::VP9, - FrameFormat::Yuv422, - FrameFormat::Uyv422, + FrameFormat::Yuy2_422, + FrameFormat::Uyvy_422, FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Yv12, @@ -147,18 +152,22 @@ pub struct PlatformSpecific { } impl PlatformSpecific { + #[must_use] pub fn new(backend: ApiBackend, format: u128) -> Self { Self { backend, format } } + #[must_use] pub fn backend(&self) -> ApiBackend { self.backend } + #[must_use] pub fn format(&self) -> u128 { self.format } + #[must_use] pub fn as_tuple(&self) -> (ApiBackend, u128) { (self.backend, self.format) } diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index f4fdf47..fcb459a 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -1,5 +1,8 @@ #![deny(clippy::pedantic)] #![warn(clippy::all)] +#![allow(clippy::cast_precision_loss)] +#![allow(clippy::cast_sign_loss)] +#![allow(clippy::cast_possible_truncation)] #![cfg_attr(feature = "test-fail-warning", deny(warnings))] #![cfg_attr(feature = "docs-features", feature(doc_cfg))] /* @@ -25,5 +28,5 @@ pub mod format_request; pub mod frame_format; pub mod traits; pub mod types; -pub mod decoder; +pub mod decoders; pub mod utils; diff --git a/nokhwa-core/src/traits.rs b/nokhwa-core/src/traits.rs index 420513a..75072e8 100644 --- a/nokhwa-core/src/traits.rs +++ b/nokhwa-core/src/traits.rs @@ -15,21 +15,14 @@ */ use crate::{ - buffer::Buffer, - error::NokhwaError, - types::{ - ApiBackend, CameraControl, CameraFormat, CameraInfo, ControlValueSetter, - KnownCameraControl, Resolution, - }, + buffer::Buffer, error::NokhwaError, format_request::FormatRequest, types::{ + ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter, KnownCameraControl, Resolution + } }; use std::{borrow::Cow, collections::HashMap}; use crate::frame_format::FrameFormat; use crate::types::FrameRate; -pub trait Backend { - const BACKEND: ApiBackend; -} - /// This trait is for any backend that allows you to grab and take frames from a camera. /// Many of the backends are **blocking**, if the camera is occupied the library will block while it waits for it to become available. /// @@ -38,9 +31,6 @@ pub trait Backend { /// - Behaviour can differ from backend to backend. While the Camera struct abstracts most of this away, if you plan to use the raw backend structs please read the `Quirks` section of each backend. /// - If you call [`stop_stream()`](CaptureTrait::stop_stream()), you will usually need to call [`open_stream()`](CaptureTrait::open_stream()) to get more frames from the camera. pub trait CaptureTrait { - /// Initialize the camera, preparing it for use, with a random format (usually the first one). - fn init(&mut self) -> Result<(), NokhwaError>; - /// Returns the current backend used. fn backend(&self) -> ApiBackend; @@ -235,6 +225,14 @@ pub trait CaptureTrait { fn stop_stream(&mut self) -> Result<(), NokhwaError>; } +/// A trait to open the capture backend. +pub trait OpenCaptureTrait: CaptureTrait { + /// Opens a camera. + /// # Errors + /// Please see implementations for errors. + fn open(index: &CameraIndex, camera_fmt: FormatRequest) -> Result where Self: Sized; +} + impl From for Box where T: CaptureTrait + 'static, @@ -247,9 +245,6 @@ where #[cfg(feature = "async")] #[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncCaptureTrait: CaptureTrait { - /// Initialize the camera, preparing it for use, with a random format (usually the first one). - async fn init_async(&mut self) -> Result<(), NokhwaError>; - /// Forcefully refreshes the stored camera format, bringing it into sync with "reality" (current camera state) /// # Errors /// If the camera can not get its most recent [`CameraFormat`]. this will error. @@ -271,16 +266,6 @@ pub trait AsyncCaptureTrait: CaptureTrait { fourcc: FrameFormat, ) -> Result>, NokhwaError>; - /// Gets the compatible [`CameraFormat`] of the camera - /// # Errors - /// If it fails to get, this will error. - async fn compatible_camera_formats_async(&mut self) -> Result, NokhwaError>; - - /// A Vector of compatible [`FrameFormat`]s. Will only return 2 elements at most. - /// # Errors - /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](NokhwaError::UnsupportedOperationError)). - async fn compatible_fourcc_async(&mut self) -> Result, NokhwaError>; - /// Will set the current [`Resolution`] /// This will reset the current stream if used while stream is opened. /// @@ -353,7 +338,11 @@ where } } +/// Capture one frame from the camera and immediately stop. pub trait OneShot: CaptureTrait { + /// Captures one frame from the camera, returning a [`Buffer`] + /// # Errors + /// If opening the stream or closing the stream has an error, this will also fail. fn one_shot(&mut self) -> Result { if self.is_stream_open() { self.frame() @@ -381,7 +370,19 @@ pub trait AsyncOneShot: AsyncCaptureTrait { } } -pub trait VirtualBackendTrait {} + +#[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] +/// A trait to open the capture backend. +pub trait AsyncOpenCaptureTrait: AsyncCaptureTrait { + /// Opens a camera. + /// # Errors + /// Please see implementations for errors. + async fn open(index: &CameraIndex, camera_fmt: FormatRequest) -> Result where Self: Sized; +} + + +// pub trait VirtualBackendTrait {} pub trait Distance where T: PartialEq { fn distance_from(&self, other: &Self) -> T; diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 57a57a4..a01170a 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -1,18 +1,15 @@ use crate::{ error::NokhwaError, - frame_format::{FrameFormat}, + frame_format::FrameFormat, }; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use std::{ - fmt::{ + borrow::Borrow, cmp::Ordering, fmt::{ Debug, Display, Formatter - }, - borrow::Borrow, - cmp::Ordering, - hash::{Hash, Hasher} + }, hash::{Hash, Hasher}, ops::{Add, Deref, DerefMut, Sub} }; use crate::traits::Distance; @@ -26,7 +23,7 @@ pub struct Range lower_inclusive: bool, maximum: Option, upper_inclusive: bool, - preferred: Option, + preferred: T, } impl Range @@ -34,7 +31,7 @@ where T: Copy + Clone + Debug + PartialOrd + PartialEq, { /// Create an upper and lower inclusive [`Range`] - pub fn new(preferred: Option, min: Option, max: Option) -> Self { + pub fn new(preferred: T, min: Option, max: Option) -> Self { Self { minimum: min, lower_inclusive: true, @@ -45,7 +42,7 @@ where } pub fn with_inclusive( - preferred: Option, + preferred: T, min: Option, lower_inclusive: bool, max: Option, @@ -60,21 +57,19 @@ where } } - pub fn with_preferred(preferred: T) -> Self { + pub fn exact(preferred: T) -> Self { Self { minimum: None, lower_inclusive: true, maximum: None, upper_inclusive: true, - preferred: Some(preferred), + preferred, } } pub fn in_range(&self, item: T) -> bool { - if let Some(pref) = self.preferred { - if pref == item { - return true - } + if item == self.preferred { + return true } if let Some(min) = self.minimum { @@ -116,11 +111,7 @@ where self.upper_inclusive = upper_inclusive; } pub fn set_preferred(&mut self, preferred: T) { - self.preferred = Some(preferred); - } - - pub fn reset_preferred(&mut self) { - self.preferred = None; + self.preferred = preferred; } pub fn minimum(&self) -> Option { self.minimum @@ -134,7 +125,7 @@ where pub fn upper_inclusive(&self) -> bool { self.upper_inclusive } - pub fn preferred(&self) -> Option { + pub fn preferred(&self) -> T { self.preferred } } @@ -149,7 +140,7 @@ where lower_inclusive: true, maximum: None, upper_inclusive: true, - preferred: None, + preferred: T::default(), } } } @@ -158,7 +149,7 @@ where /// Describes the index of the camera. /// - Index: A numbered index -/// - String: A string, used for `IPCameras`. +/// - String: A string, used for `IPCameras` or on the Browser as DeviceIDs. #[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum CameraIndex { @@ -237,7 +228,6 @@ impl TryFrom for usize { /// Note: the [`Ord`] implementation of this struct is flipped from highest to lowest. /// # JS-WASM /// This is exported as `JSResolution` -#[cfg_attr(feature = "output-wasm", wasm_bindgen)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] pub struct Resolution { @@ -245,13 +235,9 @@ pub struct Resolution { pub height_y: u32, } -#[cfg_attr(feature = "output-wasm", wasm_bindgen)] impl Resolution { /// Create a new resolution from 2 image size coordinates. - /// # JS-WASM - /// This is exported as a constructor for [`Resolution`]. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(constructor))] pub fn new(x: u32, y: u32) -> Self { Resolution { width_x: x, @@ -260,20 +246,14 @@ impl Resolution { } /// Get the width of Resolution - /// # JS-WASM - /// This is exported as `get_Width`. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Width))] #[inline] pub fn width(self) -> u32 { self.width_x } /// Get the height of Resolution - /// # JS-WASM - /// This is exported as `get_Height`. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Height))] #[inline] pub fn height(self) -> u32 { self.height_y @@ -281,7 +261,6 @@ impl Resolution { /// Get the x (width) of Resolution #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(skip))] #[inline] pub fn x(self) -> u32 { self.width_x @@ -289,11 +268,15 @@ impl Resolution { /// Get the y (height) of Resolution #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(skip))] #[inline] pub fn y(self) -> u32 { self.height_y } + + #[must_use] + pub fn aspect_ratio(&self) -> f64 { + f64::from(self.width_x) / f64::from(self.height_y) + } } impl Display for Resolution { @@ -330,139 +313,95 @@ impl Distance for Resolution { } } -#[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -/// The frame rate of a camera. DO NOT CONSTRUCT THIS ENUM DIRECTLY. YOU WILL VIOLATE INVARIANTS. Use [`FrameRate::new_integer`], [`FrameRate::new_fraction`], or [`FrameRate::new_float`] instead. -pub enum FrameRate { - /// The driver reports the frame rate as a clean integer (e.g. 30 FPS). - Integer(u32), - /// The driver reports the frame rate as a floating point number (e.g. 29.97 FPS) - Float(f32), - /// The driver reports the frame rate as a fraction (e.g. 2997/1000 FPS) - Fraction { - numerator: u16, - denominator: u16, - } -} +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct FrameRate(pub f32); impl FrameRate { - pub fn new_integer(fps: u32) -> Result { - if fps == 0 { - return Err(NokhwaError::StructureError { structure: "FrameRate".to_string(), error: "Framerate cannot be 0".to_string() }) - } - - Ok(FrameRate::Integer(fps)) + #[must_use] + pub fn new(fps: f32) -> Self { + Self(fps) } - pub fn new_float(fps: f32) -> Result { - if fps.is_nan() || fps.is_infinite() || fps.is_sign_negative() || (fps > f32::EPSILON) { - return Err(NokhwaError::StructureError { structure: "FrameRate".to_string(), error: "Invalid F32 FrameRate".to_string() }) - } - - Ok(FrameRate::Float(fps)) + #[must_use] + pub fn frame_rate(&self) -> f32 { + self.0 } +} - pub fn new_fraction(numerator: u16, denominator: u16) -> Result { - if numerator == 0 || denominator == 0 { - return Err(NokhwaError::StructureError { structure: "FrameRate".to_string(), error: "Invalid Fraction (denominator or numerator is 0)".to_string() }) - } - - Ok( - FrameRate::Fraction { - numerator, - denominator, - } - ) +impl Deref for FrameRate { + type Target = f32; + + fn deref(&self) -> &Self::Target { + &self.0 } +} - pub fn as_float(&self) -> f32 { - match self { - FrameRate::Integer(fps) => *fps as f32, - FrameRate::Float(fps) => *fps, - FrameRate::Fraction { numerator, denominator } => (*numerator as f32) / (*denominator as f32) - } +impl DerefMut for FrameRate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } +} - pub fn as_u32(&self) -> u32 { - match self { - FrameRate::Integer(fps) => *fps, - FrameRate::Float(fps) => *fps as u32, - FrameRate::Fraction { numerator, denominator } => (numerator / denominator) as u32, - } +impl Hash for FrameRate { + fn hash(&self, state: &mut H) { + state.write_u32(self.0.to_bits()); } } impl Default for FrameRate { fn default() -> Self { - FrameRate::Integer(30) + FrameRate(30.0) } } -impl PartialOrd for FrameRate { - fn partial_cmp(&self, other: &Self) -> Option { - let this_float = self.as_float(); - let other = other.as_float(); - this_float.partial_cmp(&other) +impl Display for FrameRate { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{} FPS", self.0) } } -impl Hash for FrameRate { - fn hash(&self, state: &mut H) { - match self { - FrameRate::Integer(i) => { - state.write_u32(*i) - } - FrameRate::Float(f) => { - state.write(f.to_string().as_bytes()) - } - FrameRate::Fraction { denominator, numerator } => { - state.write_u16(*denominator); - state.write_u16(*numerator); - } - } +impl Add for FrameRate { + type Output = FrameRate; + + fn add(self, rhs: Self) -> Self::Output { + (self.0 + rhs.0).into() } } -impl Display for FrameRate { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FrameRate::Integer(fps) => write!(f, "Framerate: {fps} FPS"), - FrameRate::Float(fps) => write!(f, "Framerate: {fps} FPS"), - FrameRate::Fraction { .. } => { - let as_float = self.as_float(); - write!(f, "Framerate: {as_float} FPS") - } - } +impl Add for &FrameRate { + type Output = FrameRate; + + fn add(self, rhs: Self) -> Self::Output { + (self.0 + rhs.0).into() } } -impl Distance for FrameRate { - fn distance_from(&self, other: &Self) -> f32 { - let self_as_float = self.as_float(); - let other_as_float = other.as_float(); - self_as_float.powi(2) + other_as_float.powi(2) +impl Sub for FrameRate { + type Output = FrameRate; + + fn sub(self, rhs: Self) -> Self::Output { + (self.0 - rhs.0).into() } } -impl From for FrameRate { - fn from(value: u32) -> Self { - FrameRate::Integer(value) +impl Sub for &FrameRate { + type Output = FrameRate; + + fn sub(self, rhs: Self) -> Self::Output { + (self.0 - rhs.0).into() } } impl From for FrameRate { fn from(value: f32) -> Self { - FrameRate::Float(value) + Self(value) } } -impl From<(u16, u16)> for FrameRate { - fn from(value: (u16, u16)) -> Self { - FrameRate::Fraction { - numerator: value.0, - denominator: value.1, - } +impl From for f32 { + fn from(value: FrameRate) -> Self { + value.0 } } @@ -551,7 +490,7 @@ impl Default for CameraFormat { CameraFormat { resolution: Resolution::new(640, 480), format: FrameFormat::MJpeg, - frame_rate: FrameRate::Integer(30), + frame_rate: FrameRate(30.), } } } @@ -570,7 +509,6 @@ impl Display for CameraFormat { /// `description` amd `misc` may contain information that may differ from backend to backend. Refer to each backend for details. /// `index` is a camera's index given to it by (usually) the OS usually in the order it is known to the system. #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)] -#[cfg_attr(feature = "output-wasm", wasm_bindgen)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct CameraInfo { human_name: String, @@ -579,13 +517,11 @@ pub struct CameraInfo { index: CameraIndex, } -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_class = CameraInfo))] impl CameraInfo { /// Create a new [`CameraInfo`]. /// # JS-WASM /// This is exported as a constructor for [`CameraInfo`]. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(constructor))] // OK, i just checkeed back on this code. WTF was I on when I wrote `&(impl AsRef + ?Sized)` ???? // I need to get on the same shit that my previous self was on, because holy shit that stuff is strong as FUCK! // Finally fixed this insanity. Hopefully I didnt torment anyone by actually putting this in a stable release. @@ -602,10 +538,6 @@ impl CameraInfo { /// # JS-WASM /// This is exported as a `get_HumanReadableName`. #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = HumanReadableName) - )] // yes, i know, unnecessary alloc this, unnecessary alloc that // but wasm bindgen pub fn human_name(&self) -> String { @@ -615,10 +547,6 @@ impl CameraInfo { /// Set the device info's human name. /// # JS-WASM /// This is exported as a `set_HumanReadableName`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = HumanReadableName) - )] pub fn set_human_name(&mut self, human_name: &str) { self.human_name = human_name.to_string(); } @@ -627,7 +555,6 @@ impl CameraInfo { /// # JS-WASM /// This is exported as a `get_Description`. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Description))] pub fn description(&self) -> &str { self.description.borrow() } @@ -635,7 +562,6 @@ impl CameraInfo { /// Set the device info's description. /// # JS-WASM /// This is exported as a `set_Description`. - #[cfg_attr(feature = "output-wasm", wasm_bindgen(setter = Description))] pub fn set_description(&mut self, description: &str) { self.description = description.to_string(); } @@ -644,7 +570,6 @@ impl CameraInfo { /// # JS-WASM /// This is exported as a `get_MiscString`. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = MiscString))] pub fn misc(&self) -> String { self.misc.clone() } @@ -652,7 +577,6 @@ impl CameraInfo { /// Set the device info's misc. /// # JS-WASM /// This is exported as a `set_MiscString`. - #[cfg_attr(feature = "output-wasm", wasm_bindgen(setter = MiscString))] pub fn set_misc(&mut self, misc: &str) { self.misc = misc.to_string(); } @@ -661,7 +585,6 @@ impl CameraInfo { /// # JS-WASM /// This is exported as a `get_Index`. #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Index))] pub fn index(&self) -> &CameraIndex { &self.index } @@ -669,7 +592,6 @@ impl CameraInfo { /// Set the device info's index. /// # JS-WASM /// This is exported as a `set_Index`. - #[cfg_attr(feature = "output-wasm", wasm_bindgen(setter = Index))] pub fn set_index(&mut self, index: CameraIndex) { self.index = index; } @@ -725,6 +647,7 @@ pub enum KnownCameraControl { Exposure, Iris, Focus, + Facing, /// Other camera control. Listed is the ID. /// Wasteful, however is needed for a unified API across Windows, Linux, and MacOSX due to Microsoft's usage of GUIDs. /// @@ -734,8 +657,8 @@ pub enum KnownCameraControl { /// All camera controls in an array. #[must_use] -pub const fn all_known_camera_controls() -> [KnownCameraControl; 15] { - [ +pub const fn all_known_camera_controls() -> &'static [KnownCameraControl] { + &[ KnownCameraControl::Brightness, KnownCameraControl::Contrast, KnownCameraControl::Hue, @@ -751,6 +674,7 @@ pub const fn all_known_camera_controls() -> [KnownCameraControl; 15] { KnownCameraControl::Exposure, KnownCameraControl::Iris, KnownCameraControl::Focus, + KnownCameraControl::Facing, ] } @@ -1402,7 +1326,27 @@ impl Display for ControlValueSetter { } /// The list of known capture backends to the library.
-/// - `AUTO` is special - it tells the Camera struct to automatically choose a backend most suited for the current platform. +/// - `Auto` - Use automatic selection. +/// - `AVFoundation` - Uses `AVFoundation` on `MacOSX` +/// - `Video4Linux` - `Video4Linux2`, a linux specific backend. +/// - `UniversalVideoClass` - ***DEPRECATED*** Universal Video Class (please check [libuvc](https://github.com/libuvc/libuvc)). Platform agnostic, although on linux it needs `sudo` permissions or similar to use. +/// - `MediaFoundation` - Microsoft Media Foundation, Windows only, +/// - `OpenCv` - Uses `OpenCV` to capture. Platform agnostic. +/// - `GStreamer` - ***DEPRECATED*** Uses `GStreamer` RTP to capture. Platform agnostic. +/// - `Browser` - Uses browser APIs to capture from a webcam. +pub enum SelectableBackend { + Auto, + Custom(&'static str), + AVFoundation, + Video4Linux, + UniversalVideoClass, + MediaFoundation, + OpenCv, + GStreamer, + Browser, +} + +/// The list of known capture backends to the library.
/// - `AVFoundation` - Uses `AVFoundation` on `MacOSX` /// - `Video4Linux` - `Video4Linux2`, a linux specific backend. /// - `UniversalVideoClass` - ***DEPRECATED*** Universal Video Class (please check [libuvc](https://github.com/libuvc/libuvc)). Platform agnostic, although on linux it needs `sudo` permissions or similar to use. @@ -1413,7 +1357,6 @@ impl Display for ControlValueSetter { #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum ApiBackend { - Auto, Custom(&'static str), AVFoundation, Video4Linux, @@ -1495,7 +1438,7 @@ impl Display for ApiBackend { // } // } -#[cfg(all(feature = "mjpeg", not(target_arch = "wasm")))] +#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))] #[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))] #[inline] fn decompress<'a>( @@ -1538,7 +1481,7 @@ fn decompress<'a>( /// # Safety /// This function uses `unsafe`. The caller must ensure that: /// - The input data is of the right size, does not exceed bounds, and/or the final size matches with the initial size. -#[cfg(all(feature = "mjpeg", not(target_arch = "wasm")))] +#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))] #[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))] #[inline] pub fn mjpeg_to_rgb(data: &[u8], rgba: bool) -> Result, NokhwaError> { @@ -1564,7 +1507,11 @@ pub fn mjpeg_to_rgb(data: &[u8], rgba: bool) -> Result, NokhwaError> { } } -#[cfg(not(all(feature = "mjpeg", not(target_arch = "wasm"))))] + +/// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer. +/// # Errors +/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error. +#[cfg(not(all(feature = "conversions", not(target_arch = "wasm32"))))] pub fn mjpeg_to_rgb(_data: &[u8], _rgba: bool) -> Result, NokhwaError> { Err(NokhwaError::NotImplementedError( "Not available on WASM".to_string(), @@ -1574,11 +1521,11 @@ pub fn mjpeg_to_rgb(_data: &[u8], _rgba: bool) -> Result, NokhwaError> { /// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer. /// # Errors /// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error. -#[cfg(all(feature = "mjpeg", not(target_arch = "wasm")))] +#[cfg(not(all(feature = "conversions", not(target_arch = "wasm32"))))] #[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))] #[inline] pub fn buf_mjpeg_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> { - let mut jpeg_decompress = decompress(data, rgba)?; + let mut jpeg_decompress = mozjpeg::decompress(data, rgba)?; // assert_eq!(dest.len(), jpeg_decompress.min_flat_buffer_size()); if dest.len() != jpeg_decompress.min_flat_buffer_size() { @@ -1601,7 +1548,11 @@ pub fn buf_mjpeg_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), Ok(()) } -#[cfg(not(all(feature = "mjpeg", not(target_arch = "wasm"))))] +// TODO: deprecate? +/// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer. +/// # Errors +/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error. +#[cfg(all(feature = "conversions", not(target_arch = "wasm32")))] pub fn buf_mjpeg_to_rgb(_data: &[u8], _dest: &mut [u8], _rgba: bool) -> Result<(), NokhwaError> { Err(NokhwaError::NotImplementedError( "Not available on WASM".to_string(), @@ -1632,17 +1583,17 @@ pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<() let mut buf: Vec = Vec::new(); if data.len() % 4 != 0 { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::Yuv422.into(), + src: FrameFormat::Yuy2_422, destination: "RGB888".to_string(), error: "Assertion failure, the YUV stream isn't 4:2:2! (wrong number of bytes)" .to_string(), }); } for chunk in data.chunks_exact(4) { - let y0 = chunk[0] as f32; - let u = chunk[1] as f32; - let y1 = chunk[2] as f32; - let v = chunk[3] as f32; + let y0 = f32::from(chunk[0]); + let u = f32::from(chunk[1]); + let y1 = f32::from(chunk[2]); + let v = f32::from(chunk[3]); let r0 = y0 + 1.370_705 * (v - 128.); let g0 = y0 - 0.698_001 * (v - 128.) - 0.337_633 * (u - 128.); diff --git a/nokhwa-core/src/utils.rs b/nokhwa-core/src/utils.rs index 35bf3e7..3a50ba9 100644 --- a/nokhwa-core/src/utils.rs +++ b/nokhwa-core/src/utils.rs @@ -11,7 +11,7 @@ pub fn min_max_range + Sized>(min: N, max: N break } - nums.push(counter) + nums.push(counter); } nums diff --git a/src/backends/capture/browser_camera.rs b/src/backends/capture/browser_camera.rs index bdf815e..92c4b0e 100644 --- a/src/backends/capture/browser_camera.rs +++ b/src/backends/capture/browser_camera.rs @@ -1,410 +1,295 @@ -use async_trait::async_trait; -use js_sys::{Array, Function, Map, Reflect}; -use nokhwa_core::buffer::Buffer; -use nokhwa_core::error::NokhwaError; -use nokhwa_core::format_request::FormatFilter; -use nokhwa_core::frame_format::{FrameFormat, SourceFrameFormat}; -use nokhwa_core::traits::{AsyncCaptureTrait, Backend, CaptureTrait}; -use nokhwa_core::types::{ - ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter, - KnownCameraControl, KnownCameraControlFlag, Resolution, -}; -use nokhwa_core::utils::min_max_range; use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::future::Future; -use wasm_bindgen::{JsCast, JsValue}; +use std::collections::HashMap; +use js_sys::wasm_bindgen::{JsCast, JsValue}; +use js_sys::{Array, Map, Object, Promise}; +use nokhwa_core::format_request::FormatRequest; +use serde::{de, Serialize}; use wasm_bindgen_futures::JsFuture; -use web_sys::{ - CanvasRenderingContext2d, Document, Element, HtmlCanvasElement, MediaDeviceInfo, - MediaDeviceKind, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, - MediaTrackSettings, Navigator, OffscreenCanvas, Window, -}; - -macro_rules! jsv { - ($value:expr) => {{ - JsValue::from($value) - }}; -} - -macro_rules! obj { - ($(($key:expr, $value:expr)),+ ) => {{ - use js_sys::{Map, Object}; - use wasm_bindgen::JsValue; - - let map = Map::new(); - $( - map.set(&jsv!($key), &jsv!($value)); - )+ - Object::from(map) - }}; - ($object:expr, $(($key:expr, $value:expr)),+ ) => {{ - use js_sys::{Map, Object}; - use wasm_bindgen::JsValue; - - let map = Map::new(); - $( - map.set(&jsv!($key), &jsv!($value)); - )+ - let o = Object::from(map); - Object::assign(&$object, &o) - }}; -} - -fn window() -> Result { - match web_sys::window() { - Some(win) => Ok(win), - None => Err(NokhwaError::StructureError { - structure: "web_sys Window".to_string(), - error: "None".to_string(), - }), +use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator}; +use nokhwa_core::buffer::Buffer; +use nokhwa_core::error::NokhwaError; +use nokhwa_core::frame_format::FrameFormat; +use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait}; +use nokhwa_core::types::{ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter, FrameRate, KnownCameraControl, Resolution}; + +async fn resolve_to(promise: Promise) -> Result { + let future = JsFuture::from(promise); + let jsv = match future.await { + Ok(v) => v, + Err(why) => NokhwaError::ConversionError(why.as_string().unwrap_or_default()) + }; + // we do a little checking + if !T::has_type(&jsv) { + return Err(NokhwaError::ConversionError("Bad Conversion - No Type".to_string())) } + Ok(unsafe { cast_js_value(v) }) } -fn media_devices(navigator: &Navigator) -> Result { - match navigator.media_devices() { - Ok(media) => Ok(media), - Err(why) => Err(NokhwaError::StructureError { - structure: "MediaDevices".to_string(), - error: format!("{why:?}"), - }), +fn checked_js_cast(from: JsValue) -> Result { + // we do a little checking + if !T::has_type(&jsv) { + return Err(NokhwaError::ConversionError("Bad Conversion - No Type".to_string())) } + Ok(unsafe { cast_js_value(v) }) } -fn document(window: &Window) -> Result { - match window.document() { - Some(doc) => Ok(doc), - None => Err(NokhwaError::StructureError { - structure: "web_sys Document".to_string(), - error: "None".to_string(), - }), - } +// PLEASE CHECK WHAT YOU'RE DOING OH MY GOD +unsafe fn cast_js_value(from: JsValue) -> T { + JsCast::unchecked_from_js(from) } -fn document_select_elem(doc: &Document, element: &str) -> Result { - match doc.get_element_by_id(element) { - Some(elem) => Ok(elem), - None => { - return Err(NokhwaError::StructureError { - structure: format!("Document {element}"), - error: "None".to_string(), - }) - } +// wasm-bindgen doesnt allow us to access internal attributes for some reason +// because of this, we turn objects into Map. (a JS HashMap) +fn make_jsobj_map(from: impl AsRef) -> Result { + let kvpairs = Object::entries(from.as_ref()); + // we get the constructor for a map + let map_constructor = Map::new().constructor(); + // pray we arnt in strict mode + match map_constructor.call1(&JsValue::null(), &kvpairs) { + Ok(m) => unsafe { Ok(cast_js_value::(m)) }, + Err(why) => Err(NokhwaError::ConversionError("failed to construct map to access int. values.".to_string())), } -} -fn element_cast(from: T, name: &str) -> Result { - if !from.has_type::() { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast - No Subtype".to_string(), - }); - } - - let casted = match from.dyn_into::() { - Ok(cast) => cast, - Err(_) => { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Casting Error".to_string(), - }); - } - }; - Ok(casted) } -fn element_cast_ref<'a, T: JsCast, U: JsCast>( - from: &'a T, - name: &'a str, -) -> Result<&'a U, NokhwaError> { - if !from.has_type::() { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast - No Subtype".to_string(), - }); - } - - match from.dyn_ref::() { - Some(v_e) => Ok(v_e), - None => Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast".to_string(), - }), - } +#[derive(Serialize)] +struct ConstrainedDouble { + pub min: Option, + pub ideal: Option, + pub max: Option, + pub exact: Option, } -fn create_element(doc: &Document, element: &str) -> Result { - match Document::create_element(doc, element) { - // ???? thank you intellij - Ok(new_element) => Ok(new_element), - Err(why) => Err(NokhwaError::StructureError { - structure: "Document Video Element".to_string(), - error: format!("{:?}", why.as_string()), - }), +impl Default for ConstrainedDouble { + fn default() -> Self { + Self { min: None, ideal: None, max: None, exact: None } } } -fn set_autoplay_inline(element: &Element) -> Result<(), NokhwaError> { - if let Err(why) = element.set_attribute("autoplay", "autoplay") { - return Err(NokhwaError::SetPropertyError { - property: "Video-autoplay".to_string(), - value: "autoplay".to_string(), - error: format!("{why:?}"), - }); +impl From<&ConstrainedDouble> for JsValue { + fn from(value: &ConstrainedDouble) -> Self { + serde_wasm_bindgen::to_value(value).unwrap() } - - if let Err(why) = element.set_attribute("playsinline", "playsinline") { - return Err(NokhwaError::SetPropertyError { - property: "Video-playsinline".to_string(), - value: "playsinline".to_string(), - error: format!("{why:?}"), - }); - } - - Ok(()) } -fn control_to_str(control: KnownCameraControl) -> &'static str { - match control { - KnownCameraControl::Brightness => "brightness", - KnownCameraControl::Contrast => "contrast", - KnownCameraControl::Hue => "colorTemprature", - KnownCameraControl::Saturation => "saturation", - KnownCameraControl::Sharpness => "sharpness", - KnownCameraControl::Gamma => "exposureTime", - KnownCameraControl::WhiteBalance => "whiteBalanceMode", - KnownCameraControl::BacklightComp => "exposureCompensation", - KnownCameraControl::Gain => "iso", - KnownCameraControl::Pan => "pan", - KnownCameraControl::Tilt => "tilt", - KnownCameraControl::Zoom => "zoom", - KnownCameraControl::Exposure => "exposureMode", - KnownCameraControl::Iris => "focusDistance", - KnownCameraControl::Focus => "focusMode", - KnownCameraControl::Other(u) => match u { - 0 => "frameRate", - 1 => "width", - 2 => "height", - 8 => "aspectRatio", - 16 => "facingMode", - 32 => "resizeMode", - 64 => "attachedCanvasMode", - 128 => "pointsOfInterest", - 8192 => "torch", - _ => "", - }, - } +#[derive(Serialize)] +struct ConstrainedULong { + pub min: Option, + pub ideal: Option, + pub max: Option, + pub exact: Option, } -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq::Zoom)] -pub enum JSCameraFacingMode { - Any, - Environment, - User, - Left, - Right, -} -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraResizeMode { - Any, - None, - CropAndScale, +pub struct BrowserCaptureDevice { + info: CameraInfo, + group_id: String, + device_id: String, + format: CameraFormat, + media_devices: MediaDevices, + media_stream: MediaStream } -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraCanvasType { - OffScreen(OffscreenCanvas), - HtmlCanvas(HtmlCanvasElement), -} +impl BrowserCaptureDevice { + pub async fn new(index: &CameraIndex, camera_fmt: FormatRequest) -> Result{ + let nav = window().map(|x| x.navigator()).ok_or(NokhwaError::InitializeError { backend: ApiBackend::Browser, error: "No Window Object!".to_string() })?; + let media_devices = match nav.media_devices() { + Ok(m) => m, + Err(why) => return Err(NokhwaError::InitializeError { backend: ApiBackend::Browser, error: why.as_string().unwrap_or_default() }), + }; -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraMeteringMode { - None, - Manual, - OneShot, // NIKO WHERE THE FUCK ARE WE - Continuous, -} + let (group_id, device_id) = match index { + CameraIndex::Index(i) => { + return Err(NokhwaError::OpenDeviceError(i.to_string(), "Invalid Index".to_string())) + }, + CameraIndex::String(s) => { + match s.split_once(" ") { + Some((g, d)) => (g.to_string(), d.to_string()), + None => return Err(NokhwaError::OpenDeviceError(s.to_string(), "Invalid Index".to_string())) , + } + }, + }; -impl AsRef for JSCameraMeteringMode { - fn as_ref(&self) -> &str { - match self { - JSCameraMeteringMode::None => "none", - JSCameraMeteringMode::Manual => "manual", - JSCameraMeteringMode::OneShot => "single-shot", - JSCameraMeteringMode::Continuous => "continuous", - } - } -} + let mut device_info = None; + for enumed_dev in resolve_to::(media_devices.enumerate_devices()).await? { + let dev_info = unsafe { + checked_js_cast::(enumed_dev)? + }; + if dev_info.device_id() == device_id && dev_info.group_id() == group_id { + device_info = Some(dev_info) + } + }; -impl Into for JSCameraMeteringMode { - fn into(self) -> JsValue { - JsValue::from_str(self.as_ref()) - } -} + let info = match device_info { + Some(v) => { + CameraInfo::new(&v.label(), v.kind(), &v.device_id(), index) + } + None => return Err(NokhwaError::OpenDeviceError(index.to_string(), "failed to find MediaDeviceInfo".to_string())), + }; -/// Quirks: -/// - Regular [`CaptureTrait`] will block, something that is undesired in web applications. Use [`AsyncCaptureTrait`] -/// - [REQUIRES AN UP-TO-DATE BROWSER DUE TO USE OF OFFSCREEN CANVAS.](https://caniuse.com/?search=OffscreenCanvas) -/// - [`SourceFrameFormat`]/[`FrameFormat`] does NOT apply, due to browser non-support. All returned streams will be RGB (autodecoded by browser). -/// - Custom Controls -/// - aspectRatio: 8 -/// - facingMode: 16 -/// - resizeMode: 32 -/// - attachedCanvasMode: 64 -/// - pointsOfInterest: 128 -/// - exposureTime: 256 -/// - colorTemprature: 512 -/// - iso: 1024 -/// - focusDistance: 2048 -/// - zoom: 4096 -/// - torch: 8192 -pub struct BrowserCamera { - index: CameraIndex, - info: CameraInfo, - format: CameraFormat, - media_stream: MediaStream, - init: bool, - canvas_attachment: Option, - supported_controls: HashSet, - cavnas: Option, - context: Option, -} + let mut constraint = MediaStreamConstraints::new(); + let mut video_constraint = MediaTrackConstraints::new(); + + video_constraint = video_constraint.device_id(&JsValue::from_str(&device_id)); + + match camera_fmt { + FormatRequest::Closest { resolution, frame_rate, frame_format } => { + let (_aspect_ratio, width, height) = match resolution { + Some(res_range) => ( + ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(res_range.preferred().aspect_ratio()), + }, + ConstrainedDouble { + min: res_range.minimum().map(|x| x.width() as f64), + ideal: Some(res_range.preferred().width() as f64), + max: res_range.maximum().map(|x| x.width() as f64), + exact: None, + }, + ConstrainedDouble { + min: res_range.minimum().map(|x: Resolution| x.height() as f64), + ideal: Some(res_range.preferred().width() as f64), + max: res_range.maximum().map(|x| x.height() as f64), + exact: None, + }, + ), + None => ( + ConstrainedDouble::default(), ConstrainedDouble::default(), ConstrainedDouble::default() + ), + }; + + let frame_rate = match frame_rate { + Some(f) => ConstrainedDouble { + min: f.minimum().map(|x| x.frame_rate() as f64), + ideal: Some(f.preferred().frame_rate() as f64), + max: f.maximum().map(|x| x.frame_rate() as f64), + exact: None, + }, + None => ConstrainedDouble::default(), + }; -impl BrowserCamera { - pub fn new(index: &CameraIndex) -> Result { - wasm_rs_async_executor::single_threaded::block_on(Self::new_async(index)) - } - - pub async fn new_async(index: &CameraIndex) -> Result { - let window = window()?; - let media_devices = media_devices(&window.navigator())?; - - let mut video_constraints = Map::new(); - video_constraints.set(&JsValue::from_str("pan"), &JsValue::TRUE); - video_constraints.set(&JsValue::from_str("tilt"), &JsValue::TRUE); - video_constraints.set(&JsValue::from_str("zoom"), &JsValue::TRUE); - - let stream: MediaStream = match media_devices.get_user_media_with_constraints( - &MediaStreamConstraints::new().video(&video_constraints), - ) { - Ok(promise) => { - let future = JsFuture::from(promise); - match future.await { - Ok(stream) => { - let media_stream: MediaStream = MediaStream::from(stream); - media_stream - } - Err(why) => { - return Err(NokhwaError::OpenDeviceError( - "MediaDevicesGetUserMediaJsFuture".to_string(), - format!("{why:?}"), - )) - } - } + video_constraint = video_constraint.width(width.into()); + video_constraint = video_constraint.height(height.into()); + video_constraint = video_constraint.frame_rate(frame_rate.into()); } - Err(why) => { - return Err(NokhwaError::OpenDeviceError( - "MediaDevicesGetUserMedia".to_string(), - format!("{why:?}"), - )) - } - }; + FormatRequest::HighestFrameRate { frame_rate, frame_format } => { + let frame_rate = match frame_rate { + Some(f) => ConstrainedDouble { + min: f.minimum().map(|x| x.frame_rate() as f64), + ideal: Some(f.preferred().frame_rate() as f64), + max: f.maximum().map(|x| x.frame_rate() as f64), + exact: None, + }, + None => ConstrainedDouble::default(), + }; - let media_info = match media_devices.enumerate_devices() { - Ok(i) => { - let future = JsFuture::from(i); - match future.await { - Ok(devs) => { - let arr = Array::from(&devs); - match index { - CameraIndex::Index(i) => { - let dr = arr.get(i as u32); - - if dr == JsValue::UNDEFINED { - return Err(NokhwaError::StructureError { - structure: "MediaDeviceInfo".to_string(), - error: "undefined".to_string(), - }); - } - - MediaDeviceInfo::from(dr) - } - CameraIndex::String(s) => { - match arr - .iter() - .map(MediaDeviceInfo::from) - .filter(|mdi| mdi.device_id() == s) - .nth(0) - { - Some(i) => i, - None => { - return Err(NokhwaError::StructureError { - structure: "MediaDeviceInfo".to_string(), - error: "no id".to_string(), - }) - } - } - } - } - } - Err(why) => { - return Err(NokhwaError::StructureError { - structure: "MediaDeviceInfo Enumerate Devices Promise".to_string(), - error: format!("{why:?}"), - }) - } - } + video_constraint = video_constraint.frame_rate(frame_rate.into()); } - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "MediaDeviceInfo".to_string(), - error: format!("{why:?}"), - }) + FormatRequest::HighestResolution { resolution, frame_format } => { + let (_aspect_ratio, width, height) = match resolution { + Some(res_range) => ( + ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(res_range.preferred().aspect_ratio()), + }, + ConstrainedDouble { + min: res_range.minimum().map(|x| x.width() as f64), + ideal: Some(res_range.preferred().width() as f64), + max: res_range.maximum().map(|x| x.width() as f64), + exact: None, + }, + ConstrainedDouble { + min: res_range.minimum().map(|x: Resolution| x.height() as f64), + ideal: Some(res_range.preferred().width() as f64), + max: res_range.maximum().map(|x| x.height() as f64), + exact: None, + }, + ), + None => ( + ConstrainedDouble::default(), ConstrainedDouble::default(), ConstrainedDouble::default() + ), + }; + + video_constraint = video_constraint.width(width.into()); + video_constraint = video_constraint.height(height.into()); } - }; + FormatRequest::Exact { resolution, frame_rate, frame_format } => { + let (_aspect_ratio, width, height) = match resolution { + Some(res_range) => ( + ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(res_range.preferred().aspect_ratio()), + }, + ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(res_range.preferred().width() as f64), + }, + ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(res_range.preferred().width() as f64), + }, + ), + None => ( + ConstrainedDouble::default(), ConstrainedDouble::default(), ConstrainedDouble::default() + ), + }; + + let frame_rate: ConstrainedDouble = match frame_rate { + Some(f) => ConstrainedDouble { + min: None, + ideal: None, + max: None, + exact: Some(f.preferred().frame_rate() as f64), + }, + None => ConstrainedDouble::default(), + }; - let info = CameraInfo::new( - &media_info.label(), - &*media_info.kind().to_string(), - &format!( - "{}:{}", - media_info.group_id().to_string(), - media_info.device_id().to_string() - ), - index, - ); - Ok(BrowserCamera { - index: index.clone(), - info, - format: CameraFormat::default(), - init: false, - cavnas: None, - context: None, - media_stream: stream, - supported_controls: HashSet::new(), - canvas_attachment: None, - }) - } -} + video_constraint = video_constraint.width(width.into()); + video_constraint = video_constraint.height(height.into()); + video_constraint = video_constraint.frame_rate(frame_rate.into()); + } + } -impl Backend for BrowserCamera { - const BACKEND: ApiBackend = ApiBackend::Browser; -} + constraint = constraint.video(&video_constraint); -impl CaptureTrait for BrowserCamera { - fn init(&mut self) -> Result<(), NokhwaError> {} + let media_stream: MediaStream = resolve_to(media_devices.get_user_media_with_constraints(&constraint)).await?; - fn init_with_format(&mut self, format: FormatFilter) -> Result { - self.init()?; + let mut video_track: MediaStreamTrack = checked_js_cast(media_stream.get_video_tracks().get(0))?; + + resolve_to::<()>(video_track.apply_constraints_with_constraints(&video_constraint)).await?; + + let track_settings = video_track.get_settings(); + let track_settings_map = make_jsobj_map(track_settings)?; + + let format = { + let frame_rate = track_settings_map.get("frameRate").as_f64().ok_or(NokhwaError::ConversionError("failed to get frameRate as f64".to_string()))?; + let resolution_width = u32::from(track_settings_map.get("width").as_f64().ok_or(NokhwaError::ConversionError("failed to get width as f64".to_string()))?); + let resolution_length = u32::from(track_settings_map.get("length").as_f64().ok_or(NokhwaError::ConversionError("failed to get length as f64".to_string()))?); + CameraFormat::new(Resolution::new(resolution_width, resolution_length), FrameFormat::Rgb8, frame_rate) + }; + + Ok(BrowserCaptureDevice { info, media_devices, media_stream, group_id, device_id, format }) } +} + +impl CaptureTrait for BrowserCaptureDevice { fn backend(&self) -> ApiBackend { - todo!() + ApiBackend::Browser } fn camera_info(&self) -> &CameraInfo { - todo!() + &self.info } fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> { @@ -421,12 +306,12 @@ impl CaptureTrait for BrowserCamera { fn compatible_list_by_resolution( &mut self, - fourcc: SourceFrameFormat, - ) -> Result>, NokhwaError> { + fourcc: FrameFormat, + ) -> Result>, NokhwaError> { todo!() } - fn compatible_fourcc(&mut self) -> Result, NokhwaError> { + fn compatible_fourcc(&mut self) -> Result, NokhwaError> { todo!() } @@ -446,207 +331,21 @@ impl CaptureTrait for BrowserCamera { todo!() } - fn frame_format(&self) -> SourceFrameFormat { + fn frame_format(&self) -> FrameFormat { todo!() } - fn set_frame_format( - &mut self, - fourcc: impl Into, - ) -> Result<(), NokhwaError> { + fn set_frame_format(&mut self, fourcc: FrameFormat) + -> Result<(), NokhwaError> { todo!() } fn camera_control(&self, control: KnownCameraControl) -> Result { - // TODO: Werid Controls like framerate and attach support - - if let KnownCameraControl::Other(custom) = control { - if custom == 64 { - // attached canvas mode - return Ok(CameraControl::new( - KnownCameraControl::Other(64), - "AttachedCanvasMode".to_string(), - nokhwa_core::types::ControlValueDescription::String { - value: self.canvas_attachment, - default: None, - }, - vec![], - active, - )); - } - } - - let cam_str = control_to_str(contorl); - let capabilities_fn = match unsafe { Reflect::get(&self.track, "getCapabilities") } { - Ok(v) => match v.dyn_ref::() { - Some(fx) => fx, - None => { - return Err(NokhwaError::GetPropertyError { - property: "getCapabilities".to_string(), - error: "getCapabilities is not a function!".to_string(), - }) - } - }, - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "getCapabilities".to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - let capabilities = match capabilities_fn.call0(&self.track) { - Ok(c) => c, - Err(v) => NokhwaError::GetPropertyError { - property: "getCapabilities".to_string(), - error: why.as_string().unwrap_or_default(), - }, // ok i guess, thanks vscode - }; - let settings = self.track.get_settings(); - let constraint_value = match unsafe { Reflect::get(&constraints, cam_str) } { - Ok(v) => v, - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: cam_str.to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - let setting_value = match unsafe { Reflect::get(&settings, cam_str) } { - Ok(v) => v, - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: cam_str.to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - - // setting range! - if unsafe { Reflect::get(&setting_value, "min").is_ok() } { - let min = match unsafe { Reflect::get(&setting_value, "min") } { - Ok(v) => v.as_f64(), - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "min".to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - let min = match min { - Some(v) => v, - None => { - return Err(NokhwaError::GetPropertyError { - property: "min".to_string(), - error: "Not a f64! Did the API change?".to_string(), - }) - } - }; - let max = match unsafe { Reflect::get(&setting_value, "max") } { - Ok(v) => v.as_f64(), - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "max".to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - let max = match max { - Some(v) => v, - None => { - return Err(NokhwaError::GetPropertyError { - property: "max".to_string(), - error: "Not a f64! Did the API change?".to_string(), - }) - } - }; - let step = match unsafe { Reflect::get(&setting_value, "step") } { - Ok(v) => v.as_f64(), - Err(why) => { - return Err(NokhwaError::GetPropertyError { - property: "step".to_string(), - error: why.as_string().unwrap_or_default(), - }) - } - }; - let step = match step { - Some(v) => v, - None => { - return Err(NokhwaError::GetPropertyError { - property: "step".to_string(), - error: "Not a f64! Did the API change?".to_string(), - }) - } - }; - - let value = match constraint_value.as_f64() { - Some(v) => v, - None => { - return Err(NokhwaError::GetPropertyError { - property: "value".to_string(), - error: "Not a f64! Did the API change?".to_string(), - }) - } - }; - - return Ok(CameraControl::new( - control, - cam_str.to_string(), - nokhwa_core::types::ControlValueDescription::FloatRange { - min, - max, - value, - step, - default: f64::default(), - }, - vec![], - true, - )); - } - // im a sequence - else if setting_value.is_array() { - let availible = Array::from(&setting_value) - .iter() - .map(|x| match x.as_string() { - Some(v) => format!("{v}:"), - None => String::new(), - }) - .collect::(); - - let value = match constraint_value.as_string() { - Some(v) => v, - None => { - return Err(NokhwaError::GetPropertyError { - property: "value".to_string(), - error: "Not a String! Did the API change?".to_string(), - }) - } - }; - return Ok(CameraControl::new( - control, - cam_str.to_string(), - nokhwa_core::types::ControlValueDescription::StringList { value, availible }, - vec![], - true, - )); - } - // nope im a bool - else { - let is_truthy = constraint_value.is_truthy(); - return Ok(CameraControl::new( - control, - cam_str.to_string(), - nokhwa_core::types::ControlValueDescription::Boolean { - value: is_truthy, - default: false, - }, - flag, - true, - )); - } + todo!() } fn camera_controls(&self) -> Result, NokhwaError> { - // controls + todo!() } fn set_camera_control( @@ -678,264 +377,10 @@ impl CaptureTrait for BrowserCamera { } } -#[cfg(feature = "output-async")] -impl AsyncCaptureTrait for BrowserCamera { - async fn init_async(&mut self) -> Result<(), NokhwaError> { - let window = window()?; - let media_devices = media_devices(&window.navigator())?; - - // request permission for camera - - // first populate supported controls and see if we have our required controls - // required: FPS, Resolution (width + height) - // everything else is optional (whiteBalanceMode, exposureMode, focusMode, pointsOfInterest, exposureCompensation, exposureTime, colorTemprature, iso, brightness, contrast, pan, saturation, sharpness, focusDistance, tilt, zoom, torch) - - let browser_constraints = media_devices.get_supported_constraints(); - - let mut supported_constraints = HashSet::new(); - - let defaults_satisfied = { - unsafe { Reflect::get(&browser_constraints, "frameRate".into()) } - .map(|x| x.is_truthy()) - .unwrap_or(false) - && unsafe { Reflect::get(&browser_constraints, "width".into()) } - .map(|x| x.is_truthy()) - .unwrap_or(false) - && unsafe { Reflect::get(&browser_constraints, "height".into()) } - .map(|x| x.is_truthy()) - .unwrap_or(false) - }; - - if !defaults_satisfied { - return Err(NokhwaError::InitializeError { backend: ApiBackend::Browser, error: "Your browser does not support the required constraints! (frameRate, width, height)".to_string() }); - } - - // STAY ~~WHITE~~ CLEAN WITH US! JOIN ~~WHITE~~ MATCH EXPRESSION SOCIETY! - - // SAFETY: /shurg - unsafe { - // aspectRatio - if Reflect::get(&browser_constraints, "aspectRatio".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Other(8)); - } - - // facingMode - if Reflect::get(&browser_constraints, "facingMode".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Other(16)); - } - - // resizeMode - if Reflect::get(&browser_constraints, "resizeMode".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Other(32)); - } - - // attachedCanvasMode - supported_constraints.insert(KnownCameraControl::Other(64)); - - // whiteBalanceMode - if Reflect::get(&browser_constraints, "whiteBalanceMode".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::WhiteBalance); - } - - // exposureMode - if Reflect::get(&browser_constraints, "exposureMode".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Exposure); - } - - // focusMode - if Reflect::get(&browser_constraints, "focusMode".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Focus); - } - - // pointsOfInterest - if Reflect::get(&browser_constraints, "pointsOfInterest".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Other(128)); - } - - // exposureCompensation - if Reflect::get(&browser_constraints, "exposureCompensation".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::BacklightComp); - } - - // exposureTime - if Reflect::get(&browser_constraints, "exposureTime".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Gamma); - } - - // colorTemprature - if Reflect::get(&browser_constraints, "colorTemprature".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Hue); - } - - // iso - if Reflect::get(&browser_constraints, "iso".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Gain); - } - - // brightness - if Reflect::get(&browser_constraints, "brightness".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Brightness); - } - - // contrast - if Reflect::get(&browser_constraints, "contrast".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Contrast); - } - - // pan - if Reflect::get(&browser_constraints, "pan".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Pan); - } - - // saturation - if Reflect::get(&browser_constraints, "saturation".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Saturation); - } - - // sharpness - if Reflect::get(&browser_constraints, "sharpness".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Sharpness); - } - - // focusDistance - if Reflect::get(&browser_constraints, "focusDistance".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Iris); - } - - // tilt - if Reflect::get(&browser_constraints, "tilt".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Tilt); - } - - // zoom - if Reflect::get(&browser_constraints, "zoom".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Zoom); - } - - // torch - if Reflect::get(&browser_constraints, "torch".into()) - .map(|x| x.is_truthy()) - .unwrap_or(false) - { - supported_constraints.insert(KnownCameraControl::Other(8192)); - } - } - - // PUT ME INTO THE CHARLOTTE VESSEL COACH I'LL PROVE FREE WILL IS REAL - - self.supported_controls = supported_constraints; - self.init = true; - - Ok(()) - } - - async fn init_with_format_async( - &mut self, - format: FormatFilter, - ) -> Result { - self.init_async()?; - - // now we need to get all formats possible - - let frame_rates = match self - .camera_control(KnownCameraControl::Other(0))? - .description() - { - nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => { - min_max_range(min, max, step) - } - _ => Err(NokhwaError::GetPropertyError { - property: "frameRate".to_string(), - error: "Bad FrameRate Type".to_string(), - }), - }; - - let widths = match self - .camera_control(KnownCameraControl::Other(1))? - .description() - { - nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => { - min_max_range(min, max, step) - } - _ => Err(NokhwaError::GetPropertyError { - property: "width".to_string(), - error: "Bad width Type".to_string(), - }), - }; - - let heights = match self - .camera_control(KnownCameraControl::Other(2))? - .description() - { - nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => { - min_max_range(min, max, step) - } - _ => Err(NokhwaError::GetPropertyError { - property: "height".to_string(), - error: "Bad height Type".to_string(), - }), - }; - - - } +#[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl AsyncCaptureTrait for BrowserCaptureDevice { async fn refresh_camera_format_async(&mut self) -> Result<(), NokhwaError> { todo!() } @@ -944,18 +389,7 @@ impl AsyncCaptureTrait for BrowserCamera { todo!() } - async fn compatible_list_by_resolution_async( - &mut self, - fourcc: SourceFrameFormat, - ) -> Result>, NokhwaError> { - todo!() - } - - async fn compatible_camera_formats_async(&mut self) -> Result, NokhwaError> { - todo!() - } - - async fn compatible_fourcc_async(&mut self) -> Result, NokhwaError> { + async fn compatible_list_by_resolution_async(&mut self, fourcc: FrameFormat) -> Result>, NokhwaError> { todo!() } @@ -967,18 +401,11 @@ impl AsyncCaptureTrait for BrowserCamera { todo!() } - async fn set_frame_format_async( - &mut self, - fourcc: SourceFrameFormat, - ) -> Result<(), NokhwaError> { + async fn set_frame_format_async(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> { todo!() } - async fn set_camera_control_async( - &mut self, - id: KnownCameraControl, - value: ControlValueSetter, - ) -> Result<(), NokhwaError> { + async fn set_camera_control_async(&mut self, id: KnownCameraControl, value: ControlValueSetter) -> Result<(), NokhwaError> { todo!() } @@ -998,3 +425,12 @@ impl AsyncCaptureTrait for BrowserCamera { todo!() } } + + +#[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl AsyncOpenCaptureTrait for AsyncCaptureTrait { + async fn open(index: &CameraIndex, camera_fmt: FormatRequest) -> Result where Self: Sized { + Self::open(index, camera_fmt) + } +} diff --git a/src/decoders/mjpeg.rs b/src/decoders/mjpeg.rs index 17db147..6aeb2e1 100644 --- a/src/decoders/mjpeg.rs +++ b/src/decoders/mjpeg.rs @@ -1,82 +1,82 @@ -use image::{ImageBuffer, Rgb}; -use nokhwa_core::buffer::Buffer; -use nokhwa_core::decoder::{Decoder, IdemptDecoder, StaticDecoder}; -use nokhwa_core::error::NokhwaError; -use nokhwa_core::frame_format::{FrameFormat, SourceFrameFormat}; - -#[inline] -fn decompress( - data: &[u8], - rgba: bool, -) -> Result<, NokhwaError> { - use mozjpeg::Decompress; - - match Decompress::new_mem(data) { - Ok(decompress) => { - let decompressor_res = if rgba { - decompress.rgba() - } else { - decompress.rgb() - }; - match decompressor_res { - Ok(decompressor) => Ok(decompressor), - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJpeg, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - } - } - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJpeg, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - } -} - - -pub struct MJPegDecoder; - -impl Decoder for MJPegDecoder { - const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[SourceFrameFormat::FrameFormat(FrameFormat::MJpeg)]; - type Pixel = Rgb; - type Container = Vec; - type Error = NokhwaError; - - fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } - - fn predicted_size_of_frame(&mut self) -> Option { - todo!() - } -} - -impl StaticDecoder for MJPegDecoder { - fn decode_static(buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } -} - -impl IdemptDecoder for MJPegDecoder { - fn decode_nm(&self, buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_nm_to_buffer(&self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } -} +// use image::{ImageBuffer, Rgb}; +// use nokhwa_core::buffer::Buffer; +// use nokhwa_core::r#mod::{Decoder, IdemptDecoder, StaticDecoder}; +// use nokhwa_core::error::NokhwaError; +// use nokhwa_core::frame_format::{FrameFormat, SourceFrameFormat}; +// +// #[inline] +// fn decompress( +// data: &[u8], +// rgba: bool, +// ) -> Result<, NokhwaError> { +// use mozjpeg::Decompress; +// +// match Decompress::new_mem(data) { +// Ok(decompress) => { +// let decompressor_res = if rgba { +// decompress.rgba() +// } else { +// decompress.rgb() +// }; +// match decompressor_res { +// Ok(decompressor) => Ok(decompressor), +// Err(why) => { +// return Err(NokhwaError::ProcessFrameError { +// src: FrameFormat::MJpeg, +// destination: "RGB888".to_string(), +// error: why.to_string(), +// }) +// } +// } +// } +// Err(why) => { +// return Err(NokhwaError::ProcessFrameError { +// src: FrameFormat::MJpeg, +// destination: "RGB888".to_string(), +// error: why.to_string(), +// }) +// } +// } +// } +// +// +// pub struct MJPegDecoder; +// +// impl Decoder for MJPegDecoder { +// const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[SourceFrameFormat::FrameFormat(FrameFormat::MJpeg)]; +// type Pixel = Rgb; +// type Container = Vec; +// type Error = NokhwaError; +// +// fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// +// fn predicted_size_of_frame(&mut self) -> Option { +// todo!() +// } +// } +// +// impl StaticDecoder for MJPegDecoder { +// fn decode_static(buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// } +// +// impl IdemptDecoder for MJPegDecoder { +// fn decode_nm(&self, buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_nm_to_buffer(&self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// } diff --git a/src/decoders/nv12.rs b/src/decoders/nv12.rs index 618102b..04929c0 100644 --- a/src/decoders/nv12.rs +++ b/src/decoders/nv12.rs @@ -1,41 +1,41 @@ -use image::{ImageBuffer, Rgb}; -use nokhwa_core::buffer::Buffer; -use nokhwa_core::decoder::{Decoder, IdemptDecoder, StaticDecoder}; -use nokhwa_core::frame_format::SourceFrameFormat; - -pub struct NV12Decoder {} - -impl Decoder for NV12Decoder { - const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[]; - type Pixel = Rgb; - type Container = Vec; - type Error = (); - - fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } - - fn predicted_size_of_frame(&mut self) -> Option { - todo!() - } -} - -impl StaticDecoder for NV12Decoder { - fn decode_static(buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } -} - -impl IdemptDecoder for NV12Decoder { - fn decode_nm(buffer: Buffer) -> Result, Self::Error> { - todo!() - } -} \ No newline at end of file +// use image::{ImageBuffer, Rgb}; +// use nokhwa_core::buffer::Buffer; +// use nokhwa_core::r#mod::{Decoder, IdemptDecoder, StaticDecoder}; +// use nokhwa_core::frame_format::SourceFrameFormat; +// +// pub struct NV12Decoder {} +// +// impl Decoder for NV12Decoder { +// const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[]; +// type Pixel = Rgb; +// type Container = Vec; +// type Error = (); +// +// fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// +// fn predicted_size_of_frame(&mut self) -> Option { +// todo!() +// } +// } +// +// impl StaticDecoder for NV12Decoder { +// fn decode_static(buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// } +// +// impl IdemptDecoder for NV12Decoder { +// fn decode_nm(buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// } \ No newline at end of file diff --git a/src/decoders/yuyv.rs b/src/decoders/yuyv.rs index 4202bbe..35248a9 100644 --- a/src/decoders/yuyv.rs +++ b/src/decoders/yuyv.rs @@ -1,54 +1,54 @@ -use image::ImageBuffer; -use nokhwa_core::buffer::Buffer; -use nokhwa_core::decoder::{Decoder, IdemptDecoder, StaticDecoder}; -use nokhwa_core::frame_format::SourceFrameFormat; - -// For those maintaining this, I recommend you read: https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#yuy2 -// https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB -// and this too: https://stackoverflow.com/questions/16107165/convert-from-yuv-420-to-imagebgr-byte -// The YUY2(Yuv422) format is a 16 bit format. We read 4 bytes at a time to get 6 bytes of RGB888. -// First, the YUY2 is converted to YCbCr 4:4:4 (4:2:2 -> 4:4:4) -// then it is converted to 6 bytes (2 pixels) of RGB888 -/// Converts a Yuv422 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) -/// # Errors -/// This may error when the data stream size is not divisible by 4, a i32 -> u8 conversion fails, or it fails to read from a certain index. -pub struct YUYVDecoder {} - -impl Decoder for YUYVDecoder { - const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[]; - type Pixel = (); - type Container = (); - type Error = (); - - fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } - - fn predicted_size_of_frame(&mut self) -> Option { - todo!() - } -} - -impl StaticDecoder for YUYVDecoder { - fn decode_static(buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } -} - -impl IdemptDecoder for YUYVDecoder { - fn decode_nm(&self, buffer: Buffer) -> Result, Self::Error> { - todo!() - } - - fn decode_nm_to_buffer(&self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { - todo!() - } -} +// use image::ImageBuffer; +// use nokhwa_core::buffer::Buffer; +// use nokhwa_core::r#mod::{Decoder, IdemptDecoder, StaticDecoder}; +// use nokhwa_core::frame_format::SourceFrameFormat; +// +// // For those maintaining this, I recommend you read: https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#yuy2 +// // https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB +// // and this too: https://stackoverflow.com/questions/16107165/convert-from-yuv-420-to-imagebgr-byte +// // The YUY2(Yuv422) format is a 16 bit format. We read 4 bytes at a time to get 6 bytes of RGB888. +// // First, the YUY2 is converted to YCbCr 4:4:4 (4:2:2 -> 4:4:4) +// // then it is converted to 6 bytes (2 pixels) of RGB888 +// /// Converts a Yuv422 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) +// /// # Errors +// /// This may error when the data stream size is not divisible by 4, a i32 -> u8 conversion fails, or it fails to read from a certain index. +// pub struct YUYVDecoder {} +// +// impl Decoder for YUYVDecoder { +// const ALLOWED_FORMATS: &'static [SourceFrameFormat] = &[]; +// type Pixel = (); +// type Container = (); +// type Error = (); +// +// fn decode(&mut self, buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// +// fn predicted_size_of_frame(&mut self) -> Option { +// todo!() +// } +// } +// +// impl StaticDecoder for YUYVDecoder { +// fn decode_static(buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// } +// +// impl IdemptDecoder for YUYVDecoder { +// fn decode_nm(&self, buffer: Buffer) -> Result, Self::Error> { +// todo!() +// } +// +// fn decode_nm_to_buffer(&self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> { +// todo!() +// } +// } diff --git a/src/js_camera.rs b/src/js_camera.rs deleted file mode 100644 index 9186fc5..0000000 --- a/src/js_camera.rs +++ /dev/null @@ -1,2720 +0,0 @@ -/* - * Copyright 2022 l1npengtul / The Nokhwa Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! This contains all the code for using webcams in the browser. -//! -//! Anything starting with `js` is meant as a binding, a.k.a. not meant for consumption. -//! -//! This assumes that you are running a modern browser on the desktop. - -use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbImage, Rgba}; -use js_sys::{Array, JsString, Map, Object, Promise}; -use nokhwa_core::{ - error::NokhwaError, - types::{CameraIndex, CameraInfo, Resolution}, -}; -use std::{ - borrow::{Borrow, Cow}, - convert::TryFrom, - fmt::{Debug, Display, Formatter}, - ops::Deref, -}; -use wasm_bindgen::{JsCast, JsValue}; -use wasm_bindgen_futures::JsFuture; -use web_sys::{ - console::log_1, CanvasRenderingContext2d, Document, Element, HtmlCanvasElement, - HtmlVideoElement, ImageData, MediaDeviceInfo, MediaDeviceKind, MediaDevices, MediaStream, - MediaStreamConstraints, MediaStreamTrack, MediaStreamTrackState, Navigator, Node, Window, -}; -#[cfg(feature = "output-wgpu")] -use wgpu::{ - Device, Extent3d, ImageCopyTexture, ImageDataLayout, Queue, Texture, TextureAspect, - TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, -}; - -// why no code completion -// big sadger - -// intellij 2021.2 review: i like structure window, 4 pengs / 5 pengs - -macro_rules! jsv { - ($value:expr) => {{ - JsValue::from($value) - }}; -} - -macro_rules! obj { - ($(($key:expr, $value:expr)),+ ) => {{ - use js_sys::{Map, Object}; - use wasm_bindgen::JsValue; - - let map = Map::new(); - $( - map.set(&jsv!($key), &jsv!($value)); - )+ - Object::from(map) - }}; - ($object:expr, $(($key:expr, $value:expr)),+ ) => {{ - use js_sys::{Map, Object}; - use wasm_bindgen::JsValue; - - let map = Map::new(); - $( - map.set(&jsv!($key), &jsv!($value)); - )+ - let o = Object::from(map); - Object::assign(&$object, &o) - }}; -} - -fn window() -> Result { - match web_sys::window() { - Some(win) => Ok(win), - None => Err(NokhwaError::StructureError { - structure: "web_sys Window".to_string(), - error: "None".to_string(), - }), - } -} - -fn media_devices(navigator: &Navigator) -> Result { - match navigator.media_devices() { - Ok(media) => Ok(media), - Err(why) => Err(NokhwaError::StructureError { - structure: "MediaDevices".to_string(), - error: format!("{why:?}"), - }), - } -} - -fn document(window: &Window) -> Result { - match window.document() { - Some(doc) => Ok(doc), - None => Err(NokhwaError::StructureError { - structure: "web_sys Document".to_string(), - error: "None".to_string(), - }), - } -} - -fn document_select_elem(doc: &Document, element: &str) -> Result { - match doc.get_element_by_id(element) { - Some(elem) => Ok(elem), - None => { - return Err(NokhwaError::StructureError { - structure: format!("Document {element}"), - error: "None".to_string(), - }) - } - } -} - -fn element_cast(from: T, name: &str) -> Result { - if !from.has_type::() { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast - No Subtype".to_string(), - }); - } - - let casted = match from.dyn_into::() { - Ok(cast) => cast, - Err(_) => { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Casting Error".to_string(), - }); - } - }; - Ok(casted) -} - -fn element_cast_ref<'a, T: JsCast, U: JsCast>( - from: &'a T, - name: &'a str, -) -> Result<&'a U, NokhwaError> { - if !from.has_type::() { - return Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast - No Subtype".to_string(), - }); - } - - match from.dyn_ref::() { - Some(v_e) => Ok(v_e), - None => Err(NokhwaError::StructureError { - structure: name.to_string(), - error: "Cannot Cast".to_string(), - }), - } -} - -fn create_element(doc: &Document, element: &str) -> Result { - match Document::create_element(doc, element) { - // ???? thank you intellij - Ok(new_element) => Ok(new_element), - Err(why) => Err(NokhwaError::StructureError { - structure: "Document Video Element".to_string(), - error: format!("{:?}", why.as_string()), - }), - } -} - -fn set_autoplay_inline(element: &Element) -> Result<(), NokhwaError> { - if let Err(why) = element.set_attribute("autoplay", "autoplay") { - return Err(NokhwaError::SetPropertyError { - property: "Video-autoplay".to_string(), - value: "autoplay".to_string(), - error: format!("{why:?}"), - }); - } - - if let Err(why) = element.set_attribute("playsinline", "playsinline") { - return Err(NokhwaError::SetPropertyError { - property: "Video-playsinline".to_string(), - value: "playsinline".to_string(), - error: format!("{why:?}"), - }); - } - - Ok(()) -} - -/// Requests Webcam permissions from the browser using [`MediaDevices::get_user_media()`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaDevices.html#method.get_user_media) [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -pub async fn request_permission() -> Result<(), NokhwaError> { - let window: Window = window()?; - let navigator = window.navigator(); - let media_devices = media_devices(&navigator)?; - - match media_devices.get_user_media_with_constraints( - MediaStreamConstraints::new() - .video(&JsValue::from_bool(true)) - .audio(&JsValue::from_bool(false)), - ) { - Ok(promise) => { - let js_future = JsFuture::from(promise); - match js_future.await { - Ok(stream) => { - let media_stream = MediaStream::from(stream); - media_stream - .get_tracks() - .iter() - .for_each(|track| MediaStreamTrack::from(track).stop()); - Ok(()) - } - Err(why) => Err(NokhwaError::OpenStreamError(format!("{why:?}"))), - } - } - Err(why) => Err(NokhwaError::StructureError { - structure: "UserMediaPermission".to_string(), - error: format!("{why:?}"), - }), - } -} - -/// Requests Webcam permissions from the browser using [`MediaDevices::get_user_media()`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaDevices.html#method.get_user_media) [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -/// # JS-WASM -/// In exported JS bindings, the name of the function is `requestPermissions`. It may throw an exception. -#[cfg(feature = "output-wasm")] -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = requestPermissions))] -pub async fn js_request_permission() -> Result<(), JsValue> { - if let Err(why) = request_permission().await { - return Err(JsValue::from(why.to_string())); - } - Ok(()) -} - -/// Queries Cameras using [`MediaDevices::enumerate_devices()`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaDevices.html#method.enumerate_devices) [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -pub async fn query_js_cameras() -> Result, NokhwaError> { - let window: Window = window()?; - let navigator = window.navigator(); - let media_devices = media_devices(&navigator)?; - - match media_devices.enumerate_devices() { - Ok(prom) => { - let prom: Promise = prom; - let future = JsFuture::from(prom); - match future.await { - Ok(v) => { - let array: Array = Array::from(&v); - let mut device_list = vec![]; - request_permission().await.unwrap_or(()); // swallow errors - for idx_device in 0_u32..array.length() { - if MediaDeviceInfo::instanceof(&array.get(idx_device)) { - let media_device_info = - MediaDeviceInfo::unchecked_from_js(array.get(idx_device)); - - if media_device_info.kind() == MediaDeviceKind::Videoinput { - match media_devices.get_user_media_with_constraints( - MediaStreamConstraints::new() - .audio(&jsv!(false)) - .video(&jsv!(obj!(( - "deviceId", - media_device_info.device_id() - )))), - ) { - Ok(promised_stream) => { - let future_stream = JsFuture::from(promised_stream); - if let Ok(stream) = future_stream.await { - let stream = MediaStream::from(stream); - let tracks = stream.get_video_tracks(); - let first = tracks.get(0); - let name = if first.is_undefined() { - format!( - "{:?}#{}", - media_device_info.kind(), - idx_device - ) - } else { - MediaStreamTrack::from(first).label() - }; - device_list.push(CameraInfo::new( - &name, - &format!("{:?}", media_device_info.kind()), - &format!( - "{} {}", - media_device_info.group_id(), - media_device_info.device_id() - ), - CameraIndex::String(format!( - "{} {}", - media_device_info.group_id(), - media_device_info.device_id() - )), - )); - tracks - .iter() - .for_each(|t| MediaStreamTrack::from(t).stop()); - } - } - Err(_) => { - device_list.push(CameraInfo::new( - &format!( - "{:?}#{}", - media_device_info.kind(), - idx_device - ), - &format!("{:?}", media_device_info.kind()), - &format!( - "{} {}", - media_device_info.group_id(), - media_device_info.device_id() - ), - CameraIndex::String(format!( - "{} {}", - media_device_info.group_id(), - media_device_info.device_id() - )), - )); - } - } - } - } - } - Ok(device_list) - } - Err(why) => Err(NokhwaError::StructureError { - structure: "EnumerateDevicesFuture".to_string(), - error: format!("{why:?}"), - }), - } - } - Err(why) => Err(NokhwaError::StructureError { - structure: "EnumerateDevices".to_string(), - error: format!("{why:?}"), - }), - } -} - -/// Queries Cameras using [`MediaDevices::enumerate_devices()`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaDevices.html#method.enumerate_devices) [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -/// # JS-WASM -/// This is exported as `queryCameras`. It may throw an exception. -#[cfg(feature = "output-wasm")] -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = queryCameras))] -pub async fn js_query_js_cameras() -> Result { - match query_js_cameras().await { - Ok(cameras) => Ok(cameras.into_iter().map(JsValue::from).collect()), - Err(why) => Err(JsValue::from(why.to_string())), - } -} - -/// Queries the browser's supported constraints using [`navigator.mediaDevices.getSupportedConstraints()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getSupportedConstraints) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -pub fn query_supported_constraints() -> Result, NokhwaError> { - let window: Window = window()?; - let navigator = window.navigator(); - let media_devices = media_devices(&navigator)?; - - let supported_constraints = JsValue::from(media_devices.get_supported_constraints()); - let dict_supported_constraints = Object::from(supported_constraints); - - let mut capabilities_vec = vec![]; - for constraint in Object::keys(&dict_supported_constraints).iter() { - let constraint_str = JsValue::from(JsString::from(constraint)) - .as_string() - .unwrap_or_default(); - - // swallow errors - if let Ok(cap) = JSCameraSupportedCapabilities::try_from(constraint_str) { - capabilities_vec.push(cap); - } - } - Ok(capabilities_vec) -} - -/// Queries the browser's supported constraints using [`navigator.mediaDevices.getSupportedConstraints()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getSupportedConstraints) -/// # Errors -/// This will error if there is no valid web context or the web API is not supported -/// # JS-WASM -/// This is exported as `queryConstraints` and returns an array of strings. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = queryConstraints))] -pub fn query_supported_constraints_js() -> Result { - match query_supported_constraints() { - Ok(constraints) => Ok(constraints - .into_iter() - .map(|c| JsValue::from(c.to_string())) - .collect()), - Err(why) => Err(JsValue::from(why.to_string())), - } -} - -/// The enum describing the possible constraints for video in the browser. -/// - `DeviceID`: The ID of the device -/// - `GroupID`: The ID of the group that the device is in -/// - `AspectRatio`: The Aspect Ratio of the final stream -/// - `FacingMode`: What direction the camera is facing. This is more common on mobile. See [`JSCameraFacingMode`] -/// - `FrameRate`: The Frame Rate of the final stream -/// - `Height`: The height of the final stream in pixels -/// - `Width`: The width of the final stream in pixels -/// - `ResizeMode`: Whether the client can crop and/or scale the stream to match the resolution (width, height). See [`JSCameraResizeMode`] -/// See More: [`MediaTrackConstraints`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) [`Capabilities, constraints, and settings`](https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API/Constraints) -/// # JS-WASM -/// This is exported as `CameraSupportedCapabilities`. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = CameraSupportedCapabilities))] -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraSupportedCapabilities { - DeviceID, - GroupID, - AspectRatio, - FacingMode, - FrameRate, - Height, - Width, - ResizeMode, -} - -impl Display for JSCameraSupportedCapabilities { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let cap = match self { - JSCameraSupportedCapabilities::DeviceID => "deviceId", - JSCameraSupportedCapabilities::GroupID => "groupId", - JSCameraSupportedCapabilities::AspectRatio => "aspectRatio", - JSCameraSupportedCapabilities::FacingMode => "facingMode", - JSCameraSupportedCapabilities::FrameRate => "frameRate", - JSCameraSupportedCapabilities::Height => "height", - JSCameraSupportedCapabilities::Width => "width", - JSCameraSupportedCapabilities::ResizeMode => "resizeMode", - }; - - write!(f, "{cap}") - } -} - -impl Debug for JSCameraSupportedCapabilities { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = self.to_string(); - write!(f, "{str}") - } -} - -impl TryFrom for JSCameraSupportedCapabilities { - type Error = NokhwaError; - - fn try_from(value: String) -> Result { - let value = value.as_str(); - let result = match value { - "deviceId" => JSCameraSupportedCapabilities::DeviceID, - "groupId" => JSCameraSupportedCapabilities::GroupID, - "aspectRatio" => JSCameraSupportedCapabilities::AspectRatio, - "facingMode" => JSCameraSupportedCapabilities::FacingMode, - "frameRate" => JSCameraSupportedCapabilities::FrameRate, - "height" => JSCameraSupportedCapabilities::Height, - "width" => JSCameraSupportedCapabilities::Width, - "resizeMode" => JSCameraSupportedCapabilities::ResizeMode, - _ => { - return Err(NokhwaError::StructureError { - structure: "JSCameraSupportedCapabilities".to_string(), - error: "No Match Str".to_string(), - }) - } - }; - Ok(result) - } -} - -/// The Facing Mode of the camera -/// - Any: Make no particular choice. -/// - Environment: The camera that shows the user's environment, such as the back camera of a smartphone -/// - User: The camera that shows the user, such as the front camera of a smartphone -/// - Left: The camera that shows the user but to their left, such as a camera that shows a user but to their left shoulder -/// - Right: The camera that shows the user but to their right, such as a camera that shows a user but to their right shoulder -/// See More: [`facingMode`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode) -/// # JS-WASM -/// This is exported as `CameraFacingMode`. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = CameraFacingMode))] -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraFacingMode { - Any, - Environment, - User, - Left, - Right, -} - -impl Display for JSCameraFacingMode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let cap = match self { - JSCameraFacingMode::Environment => "environment", - JSCameraFacingMode::User => "user", - JSCameraFacingMode::Left => "left", - JSCameraFacingMode::Right => "right", - JSCameraFacingMode::Any => "any", - }; - write!(f, "{cap}") - } -} - -impl Debug for JSCameraFacingMode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = self.to_string(); - write!(f, "{str}") - } -} - -/// Whether the browser can crop and/or scale to match the requested resolution. -/// - `Any`: Make no particular choice. -/// - `None`: Do not crop and/or scale. -/// - `CropAndScale`: Crop and/or scale to match the requested resolution. -/// See More: [`resizeMode`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#resizemode) -/// # JS-WASM -/// This is exported as `CameraResizeMode`. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = CameraResizeMode))] -#[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum JSCameraResizeMode { - Any, - None, - CropAndScale, -} - -impl Display for JSCameraResizeMode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let cap = match self { - JSCameraResizeMode::None => "none", - JSCameraResizeMode::CropAndScale => "crop-and-scale", - JSCameraResizeMode::Any => "", - }; - - write!(f, "{cap}") - } -} - -impl Debug for JSCameraResizeMode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = self.to_string(); - write!(f, "{str}") - } -} - -/// A builder that builds a [`JSCameraConstraints`] that is used to construct a [`JSCamera`]. -/// See More: [`Constraints MDN`](https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API/Constraints), [`Properties of Media Tracks MDN`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) -/// # JS-WASM -/// This is exported as `CameraConstraintsBuilder`. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = CameraConstraintsBuilder))] -#[derive(Clone, Debug)] -#[allow(clippy::struct_excessive_bools)] -pub struct JSCameraConstraintsBuilder { - pub(crate) min_resolution: Option, - pub(crate) preferred_resolution: Resolution, - pub(crate) max_resolution: Option, - pub(crate) resolution_exact: bool, - pub(crate) min_aspect_ratio: Option, - pub(crate) aspect_ratio: f64, - pub(crate) max_aspect_ratio: Option, - pub(crate) aspect_ratio_exact: bool, - pub(crate) facing_mode: JSCameraFacingMode, - pub(crate) facing_mode_exact: bool, - pub(crate) min_frame_rate: Option, - pub(crate) frame_rate: u32, - pub(crate) max_frame_rate: Option, - pub(crate) frame_rate_exact: bool, - pub(crate) resize_mode: JSCameraResizeMode, - pub(crate) resize_mode_exact: bool, - pub(crate) device_id: String, - pub(crate) device_id_exact: bool, - pub(crate) group_id: String, - pub(crate) group_id_exact: bool, -} - -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_class = CameraConstraintsBuilder))] -impl JSCameraConstraintsBuilder { - /// Constructs a default [`JSCameraConstraintsBuilder`]. - /// The constructed default [`JSCameraConstraintsBuilder`] has these settings: - /// - 480x234 min, 640x360 ideal, 1920x1080 max - /// - 10 FPS min, 15 FPS ideal, 30 FPS max - /// - 1.0 aspect ratio min, 1.77777777778 aspect ratio ideal, 2.0 aspect ratio max - /// - No `exact`s - /// # JS-WASM - /// This is exported as a constructor. - #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(constructor))] - pub fn new() -> Self { - JSCameraConstraintsBuilder::default() - } - - /// Sets the minimum resolution for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`width`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/width) and [`height`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/height). - /// # JS-WASM - /// This is exported as `set_MinResolution`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MinResolution) - )] - pub fn min_resolution(mut self, min_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.min_resolution = Some(min_resolution); - self - } - - /// Sets the preferred resolution for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`width`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/width) and [`height`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/height). - /// # JS-WASM - /// This is exported as `set_Resolution`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = Resolution) - )] - pub fn resolution(mut self, new_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.preferred_resolution = new_resolution; - self - } - - /// Sets the maximum resolution for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`width`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/width) and [`height`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/height). - /// # JS-WASM - /// This is exported as `set_MaxResolution`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MaxResolution) - )] - pub fn max_resolution(mut self, max_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.min_resolution = Some(max_resolution); - self - } - - /// Sets whether the resolution fields ([`width`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/width), [`height`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/height)/[`resolution`](crate::js_camera::JSCameraConstraintsBuilder::resolution)) - /// should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// Note that this will make the builder ignore [`min_resolution`](crate::js_camera::JSCameraConstraintsBuilder::min_resolution) and [`max_resolution`](crate::js_camera::JSCameraConstraintsBuilder::max_resolution). - /// # JS-WASM - /// This is exported as `set_ResolutionExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = ResolutionExact) - )] - pub fn resolution_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.resolution_exact = value; - self - } - - /// Sets the minimum aspect ratio of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`aspectRatio`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/aspectRatio). - /// # JS-WASM - /// This is exported as `set_MinAspectRatio`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MinAspectRatio) - )] - pub fn min_aspect_ratio(mut self, ratio: f64) -> JSCameraConstraintsBuilder { - self.min_aspect_ratio = Some(ratio); - self - } - - /// Sets the aspect ratio of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`aspectRatio`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/aspectRatio). - /// # JS-WASM - /// This is exported as `set_AspectRatio`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = AspectRatio) - )] - pub fn aspect_ratio(mut self, ratio: f64) -> JSCameraConstraintsBuilder { - self.aspect_ratio = ratio; - self - } - - /// Sets the maximum aspect ratio of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`aspectRatio`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/aspectRatio). - /// # JS-WASM - /// This is exported as `set_MaxAspectRatio`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MaxAspectRatio) - )] - pub fn max_aspect_ratio(mut self, ratio: f64) -> JSCameraConstraintsBuilder { - self.max_aspect_ratio = Some(ratio); - self - } - - /// Sets whether the [`aspect_ratio`](crate::js_camera::JSCameraConstraintsBuilder::aspect_ratio) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// Note that this will make the builder ignore [`min_aspect_ratio`](crate::js_camera::JSCameraConstraintsBuilder::min_aspect_ratio) and [`max_aspect_ratio`](crate::js_camera::JSCameraConstraintsBuilder::max_aspect_ratio). - /// # JS-WASM - /// This is exported as `set_AspectRatioExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = AspectRatioExact) - )] - pub fn aspect_ratio_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.aspect_ratio_exact = value; - self - } - - /// Sets the facing mode of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`facingMode`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode). - /// # JS-WASM - /// This is exported as `set_FacingMode`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = FacingMode) - )] - pub fn facing_mode(mut self, facing_mode: JSCameraFacingMode) -> JSCameraConstraintsBuilder { - self.facing_mode = facing_mode; - self - } - - /// Sets whether the [`facing_mode`](crate::js_camera::JSCameraConstraintsBuilder::facing_mode) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// # JS-WASM - /// This is exported as `set_FacingModeExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = FacingModeExact) - )] - pub fn facing_mode_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.facing_mode_exact = value; - self - } - - /// Sets the minimum frame rate of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`frameRate`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/frameRate). - /// # JS-WASM - /// This is exported as `set_MinFrameRate`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MinFrameRate) - )] - pub fn min_frame_rate(mut self, fps: u32) -> JSCameraConstraintsBuilder { - self.min_frame_rate = Some(fps); - self - } - - /// Sets the frame rate of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`frameRate`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/frameRate). - /// # JS-WASM - /// This is exported as `set_FrameRate`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = FrameRate) - )] - pub fn frame_rate(mut self, fps: u32) -> JSCameraConstraintsBuilder { - self.frame_rate = fps; - self - } - - /// Sets the maximum frame rate of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`frameRate`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/frameRate). - /// # JS-WASM - /// This is exported as `set_MaxFrameRate`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = MaxFrameRate) - )] - pub fn max_frame_rate(mut self, fps: u32) -> JSCameraConstraintsBuilder { - self.max_frame_rate = Some(fps); - self - } - - /// Sets whether the [`frame_rate`](crate::js_camera::JSCameraConstraintsBuilder::frame_rate) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// Note that this will make the builder ignore [`min_frame_rate`](crate::js_camera::JSCameraConstraintsBuilder::min_frame_rate) and [`max_frame_rate`](crate::js_camera::JSCameraConstraintsBuilder::max_frame_rate). - /// # JS-WASM - /// This is exported as `set_FrameRateExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = FrameRateExact) - )] - pub fn frame_rate_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.frame_rate_exact = value; - self - } - - /// Sets the resize mode of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`resizeMode`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#resizemode). - /// # JS-WASM - /// This is exported as `set_ResizeMode`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = ResizeMode) - )] - pub fn resize_mode(mut self, resize_mode: JSCameraResizeMode) -> JSCameraConstraintsBuilder { - self.resize_mode = resize_mode; - self - } - - /// Sets whether the [`resize_mode`](crate::js_camera::JSCameraConstraintsBuilder::resize_mode) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// # JS-WASM - /// This is exported as `set_ResizeModeExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = ResizeModeExact) - )] - pub fn resize_mode_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.resize_mode_exact = value; - self - } - - /// Sets the device ID of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`deviceId`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/deviceId). - /// # JS-WASM - /// This is exported as `set_DeviceId`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = DeviceId) - )] - pub fn device_id(mut self, id: &str) -> JSCameraConstraintsBuilder { - self.device_id = id.to_string(); - self - } - - /// Sets whether the [`device_id`](crate::js_camera::JSCameraConstraintsBuilder::device_id) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// # JS-WASM - /// This is exported as `set_DeviceIdExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = DeviceIdExact) - )] - pub fn device_id_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.device_id_exact = value; - self - } - - /// Sets the group ID of the resulting constraint for the [`JSCameraConstraintsBuilder`]. - /// - /// Sets [`groupId`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/groupId). - /// # JS-WASM - /// This is exported as `set_GroupId`. - #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = GroupId))] - pub fn group_id(mut self, id: &str) -> JSCameraConstraintsBuilder { - self.group_id = id.to_string(); - self - } - - /// Sets whether the [`group_id`](crate::js_camera::JSCameraConstraintsBuilder::group_id) field should use [`exact`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#constraints). - /// # JS-WASM - /// This is exported as `set_GroupIdExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = GroupIdExact) - )] - pub fn group_id_exact(mut self, value: bool) -> JSCameraConstraintsBuilder { - self.group_id_exact = value; - self - } - - /// Builds the [`JSCameraConstraints`]. Wrapper for [`build`](crate::js_camera::JSCameraConstraintsBuilder::build) - /// - /// Fields that use exact are marked `exact`, otherwise are marked with `ideal`. If min-max are involved, they will use `min` and `max` accordingly. - /// # JS-WASM - /// This is exported as `buildCameraConstraints`. - #[cfg(feature = "output-wasm")] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(js_name = buildCameraConstraints) - )] - #[must_use] - pub fn js_build(self) -> JSCameraConstraints { - self.build() - } -} - -impl JSCameraConstraintsBuilder { - /// Builds the [`JSCameraConstraints`] - #[allow(clippy::too_many_lines)] - #[must_use] - pub fn build(self) -> JSCameraConstraints { - let null_resolution = Resolution::default(); - let null_string = String::new(); - - let mut video_object = Object::new(); - - // width - if self.resolution_exact { - if self.preferred_resolution != null_resolution { - video_object = obj!( - video_object, - ("width", obj!(("exact", self.preferred_resolution.width()))) - ); - } - } else { - let mut width_object = Object::new(); - - if let Some(min_res) = self.min_resolution { - width_object = obj!(width_object, ("min", min_res.width())); - } - - width_object = obj!(width_object, ("ideal", self.preferred_resolution.width())); - if let Some(max_res) = self.max_resolution { - width_object = obj!(width_object, ("max", max_res.width())); - } - - video_object = obj!(video_object, ("width", width_object)); - } - - // height - if self.resolution_exact { - if self.preferred_resolution != null_resolution { - video_object = obj!( - video_object, - ( - "height", - obj!(("exact", self.preferred_resolution.height())) - ) - ); - } - } else { - let mut height_object = Object::new(); - - if let Some(min_res) = self.min_resolution { - height_object = obj!(height_object, ("min", min_res.height())); - } - - height_object = obj!(height_object, ("ideal", self.preferred_resolution.height())); - if let Some(max_res) = self.max_resolution { - height_object = obj!(height_object, ("max", max_res.height())); - } - - video_object = obj!(video_object, ("height", height_object)); - } - - // aspect ratio - if self.aspect_ratio_exact { - if self.aspect_ratio != 0_f64 { - video_object = obj!( - video_object, - ("aspectRatio", obj!(("exact", self.aspect_ratio))) - ); - } - } else { - let mut aspect_ratio_object = Object::new(); - - if let Some(min_ratio) = self.min_aspect_ratio { - aspect_ratio_object = obj!(aspect_ratio_object, ("min", min_ratio)); - } - - aspect_ratio_object = obj!(aspect_ratio_object, ("ideal", self.aspect_ratio)); - if let Some(max_ratio) = self.max_aspect_ratio { - aspect_ratio_object = obj!(aspect_ratio_object, ("max", max_ratio)); - } - - video_object = obj!(video_object, ("aspectRatio", aspect_ratio_object)); - } - - if self.facing_mode != JSCameraFacingMode::Any && self.facing_mode_exact { - video_object = obj!( - video_object, - ("facingMode", obj!(("exact", self.facing_mode.to_string()))) - ); - } else if self.facing_mode != JSCameraFacingMode::Any { - video_object = obj!( - video_object, - ("facingMode", obj!(("ideal", self.facing_mode.to_string()))) - ); - } - - // aspect ratio - if self.frame_rate_exact { - if self.frame_rate != 0 { - video_object = obj!( - video_object, - ("frameRate", obj!(("exact", self.frame_rate))) - ); - } - } else { - let mut frame_rate_object = Object::new(); - - if let Some(min_frame_rate) = self.min_frame_rate { - frame_rate_object = obj!(frame_rate_object, ("min", min_frame_rate)); - } - - frame_rate_object = obj!(frame_rate_object, ("ideal", self.frame_rate)); - if let Some(max_frame_rate) = self.max_frame_rate { - frame_rate_object = obj!(frame_rate_object, ("max", max_frame_rate)); - } - - video_object = obj!(video_object, ("frameRate", frame_rate_object)); - } - - if self.resize_mode != JSCameraResizeMode::Any && self.resize_mode_exact { - video_object = obj!( - video_object, - ("resizeMode", obj!(("exact", self.resize_mode.to_string()))) - ); - } else if self.resize_mode != JSCameraResizeMode::Any { - video_object = obj!( - video_object, - ("resizeMode", obj!(("ideal", self.resize_mode.to_string()))) - ); - } - - if self.device_id != null_string && self.device_id_exact { - video_object = obj!(video_object, ("deviceId", obj!(("exact", &self.device_id)))); - } else if self.device_id != null_string { - video_object = obj!(video_object, ("deviceId", obj!(("ideal", &self.device_id)))); - } - - if self.group_id != null_string && self.group_id_exact { - video_object = obj!(video_object, ("groupId", obj!(("exact", &self.group_id)))); - } else if self.group_id != null_string { - video_object = obj!(video_object, ("groupId", obj!(("ideal", &self.group_id)))); - } - - let media_stream_constraints = MediaStreamConstraints::new() - .audio(&jsv!(false)) - .video(&jsv!(video_object)) - .clone(); - - JSCameraConstraints { - media_constraints: media_stream_constraints, - min_resolution: self.min_resolution, - preferred_resolution: self.preferred_resolution, - max_resolution: self.max_resolution, - resolution_exact: self.resolution_exact, - min_aspect_ratio: self.min_aspect_ratio, - aspect_ratio: self.aspect_ratio, - max_aspect_ratio: self.max_aspect_ratio, - aspect_ratio_exact: self.aspect_ratio_exact, - facing_mode: self.facing_mode, - facing_mode_exact: self.facing_mode_exact, - min_frame_rate: self.min_frame_rate, - frame_rate: self.frame_rate, - max_frame_rate: self.max_frame_rate, - frame_rate_exact: self.frame_rate_exact, - resize_mode: self.resize_mode, - resize_mode_exact: self.resize_mode_exact, - device_id: self.device_id, - device_id_exact: self.device_id_exact, - group_id: self.group_id, - group_id_exact: self.device_id_exact, - } - } -} - -impl Default for JSCameraConstraintsBuilder { - fn default() -> Self { - JSCameraConstraintsBuilder { - min_resolution: Some(Resolution::new(480, 234)), - preferred_resolution: Resolution::new(640, 360), - max_resolution: Some(Resolution::new(1920, 1080)), - resolution_exact: false, - min_aspect_ratio: Some(1_f64), - aspect_ratio: 1.777_777_777_78_f64, - max_aspect_ratio: Some(2_f64), - aspect_ratio_exact: false, - facing_mode: JSCameraFacingMode::Any, - facing_mode_exact: false, - min_frame_rate: Some(10), - frame_rate: 15, - max_frame_rate: Some(30), - frame_rate_exact: false, - resize_mode: JSCameraResizeMode::Any, - resize_mode_exact: false, - device_id: String::new(), - device_id_exact: false, - group_id: String::new(), - group_id_exact: false, - } - } -} - -/// Constraints to create a [`JSCamera`] -/// -/// If you want more options, see [`JSCameraConstraintsBuilder`] -/// # JS-WASM -/// This is exported as `CameraConstraints`. -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = CameraConstraints))] -#[derive(Clone, Debug)] -#[allow(clippy::struct_excessive_bools)] -pub struct JSCameraConstraints { - pub(crate) media_constraints: MediaStreamConstraints, - pub(crate) min_resolution: Option, - pub(crate) preferred_resolution: Resolution, - pub(crate) max_resolution: Option, - pub(crate) resolution_exact: bool, - pub(crate) min_aspect_ratio: Option, - pub(crate) aspect_ratio: f64, - pub(crate) max_aspect_ratio: Option, - pub(crate) aspect_ratio_exact: bool, - pub(crate) facing_mode: JSCameraFacingMode, - pub(crate) facing_mode_exact: bool, - pub(crate) min_frame_rate: Option, - pub(crate) frame_rate: u32, - pub(crate) max_frame_rate: Option, - pub(crate) frame_rate_exact: bool, - pub(crate) resize_mode: JSCameraResizeMode, - pub(crate) resize_mode_exact: bool, - pub(crate) device_id: String, - pub(crate) device_id_exact: bool, - pub(crate) group_id: String, - pub(crate) group_id_exact: bool, -} - -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_class = CameraConstraints))] -impl JSCameraConstraints { - /// Gets the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) - /// # JS-WASM - /// This is exported as `get_MediaStreamConstraints`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MediaStreamConstraints) - )] - pub fn media_constraints(&self) -> MediaStreamConstraints { - self.media_constraints.clone() - } - - /// Gets the minimum [`Resolution`]. - /// # JS-WASM - /// This is exported as `get_MinResolution`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MinResolution) - )] - #[must_use] - pub fn min_resolution(&self) -> Option { - self.min_resolution - } - - /// Gets the minimum [`Resolution`]. - /// # JS-WASM - /// This is exported as `set_MinResolution`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MinResolution) - )] - pub fn set_min_resolution(&mut self, min_resolution: Resolution) { - self.min_resolution = Some(min_resolution); - } - - /// Gets the internal [`Resolution`] - /// # JS-WASM - /// This is exported as `get_Resolution`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = Resolution) - )] - pub fn resolution(&self) -> Resolution { - self.preferred_resolution - } - - /// Sets the internal [`Resolution`] - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_Resolution`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = Resolution) - )] - pub fn set_resolution(&mut self, preferred_resolution: Resolution) { - self.preferred_resolution = preferred_resolution; - } - - /// Gets the maximum [`Resolution`]. - /// # JS-WASM - /// This is exported as `get_MaxResolution`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MaxResolution) - )] - #[must_use] - pub fn max_resolution(&self) -> Option { - self.max_resolution - } - - /// Gets the maximum [`Resolution`]. - /// # JS-WASM - /// This is exported as `set_MaxResolution`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MaxResolution) - )] - pub fn set_max_resolution(&mut self, max_resolution: Resolution) { - self.max_resolution = Some(max_resolution); - } - - /// Gets the internal resolution exact. - /// # JS-WASM - /// This is exported as `get_ResolutionExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = ResolutionExact) - )] - pub fn resolution_exact(&self) -> bool { - self.resolution_exact - } - - /// Sets the internal resolution exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_ResolutionExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = ResolutionExact) - )] - pub fn set_resolution_exact(&mut self, resolution_exact: bool) { - self.resolution_exact = resolution_exact; - } - - /// Gets the minimum aspect ratio of the [`JSCameraConstraints`]. - /// # JS-WASM - /// This is exported as `get_MinAspectRatio`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MinAspectRatio) - )] - pub fn min_aspect_ratio(&self) -> Option { - self.min_aspect_ratio - } - - /// Sets the minimum aspect ratio of the [`JSCameraConstraints`]. - /// # JS-WASM - /// This is exported as `set_MinAspectRatio`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MinAspectRatio) - )] - pub fn set_min_aspect_ratio(&mut self, min_aspect_ratio: f64) { - self.min_aspect_ratio = Some(min_aspect_ratio); - } - - /// Gets the internal aspect ratio. - /// # JS-WASM - /// This is exported as `get_AspectRatio`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = AspectRatio) - )] - pub fn aspect_ratio(&self) -> f64 { - self.aspect_ratio - } - - /// Sets the internal aspect ratio. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_AspectRatio`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = AspectRatio) - )] - pub fn set_aspect_ratio(&mut self, aspect_ratio: f64) { - self.aspect_ratio = aspect_ratio; - } - - /// Gets the maximum aspect ratio. - /// # JS-WASM - /// This is exported as `get_MaxAspectRatio`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MaxAspectRatio) - )] - #[must_use] - pub fn max_aspect_ratio(&self) -> Option { - self.max_aspect_ratio - } - - /// Sets the maximum internal aspect ratio. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_MaxAspectRatio`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MaxAspectRatio) - )] - pub fn set_max_aspect_ratio(&mut self, max_aspect_ratio: f64) { - self.max_aspect_ratio = Some(max_aspect_ratio); - } - - /// Gets the internal aspect ratio exact. - /// # JS-WASM - /// This is exported as `get_AspectRatioExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = AspectRatioExact) - )] - pub fn aspect_ratio_exact(&self) -> bool { - self.aspect_ratio_exact - } - - /// Sets the internal aspect ratio exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_AspectRatioExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = AspectRatioExact) - )] - pub fn set_aspect_ratio_exact(&mut self, aspect_ratio_exact: bool) { - self.aspect_ratio_exact = aspect_ratio_exact; - } - - /// Gets the internal [`JSCameraFacingMode`]. - /// # JS-WASM - /// This is exported as `get_FacingMode`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = FacingMode) - )] - pub fn facing_mode(&self) -> JSCameraFacingMode { - self.facing_mode - } - - /// Sets the internal [`JSCameraFacingMode`] - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_FacingMode`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = FacingMode) - )] - pub fn set_facing_mode(&mut self, facing_mode: JSCameraFacingMode) { - self.facing_mode = facing_mode; - } - - /// Gets the internal facing mode exact. - /// # JS-WASM - /// This is exported as `get_FacingModeExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = FacingModeExact) - )] - pub fn facing_mode_exact(&self) -> bool { - self.facing_mode_exact - } - - /// Sets the internal facing mode exact - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_FacingModeExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = FacingModeExact) - )] - pub fn set_facing_mode_exact(&mut self, facing_mode_exact: bool) { - self.facing_mode_exact = facing_mode_exact; - } - - /// Gets the minimum internal frame rate. - /// # JS-WASM - /// This is exported as `get_MinFrameRate`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MinFrameRate) - )] - #[must_use] - pub fn min_frame_rate(&self) -> Option { - self.min_frame_rate - } - - /// Sets the minimum internal frame rate - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_MinFrameRate`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MinFrameRate) - )] - pub fn set_min_frame_rate(&mut self, min_frame_rate: u32) { - self.min_frame_rate = Some(min_frame_rate); - } - - /// Gets the internal frame rate. - /// # JS-WASM - /// This is exported as `get_FrameRate`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = FrameRate) - )] - pub fn frame_rate(&self) -> u32 { - self.frame_rate - } - - /// Sets the internal frame rate - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_FrameRate`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = FrameRate) - )] - pub fn set_frame_rate(&mut self, frame_rate: u32) { - self.frame_rate = frame_rate; - } - - /// Gets the maximum internal frame rate. - /// # JS-WASM - /// This is exported as `get_MaxFrameRate`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = MaxFrameRate) - )] - #[must_use] - pub fn max_frame_rate(&self) -> Option { - self.max_frame_rate - } - - /// Sets the maximum internal frame rate - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_MaxFrameRate`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = MaxFrameRate) - )] - pub fn set_max_frame_rate(&mut self, max_frame_rate: u32) { - self.max_frame_rate = Some(max_frame_rate); - } - - /// Gets the internal frame rate exact. - /// # JS-WASM - /// This is exported as `get_FrameRateExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = FrameRateExact) - )] - pub fn frame_rate_exact(&self) -> bool { - self.frame_rate_exact - } - - /// Sets the internal frame rate exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_FrameRateExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = FrameRateExact) - )] - pub fn set_frame_rate_exact(&mut self, frame_rate_exact: bool) { - self.frame_rate_exact = frame_rate_exact; - } - - /// Gets the internal [`JSCameraResizeMode`]. - /// # JS-WASM - /// This is exported as `get_ResizeMode`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = ResizeMode) - )] - pub fn resize_mode(&self) -> JSCameraResizeMode { - self.resize_mode - } - - /// Sets the internal [`JSCameraResizeMode`] - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_ResizeMode`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = ResizeMode) - )] - pub fn set_resize_mode(&mut self, resize_mode: JSCameraResizeMode) { - self.resize_mode = resize_mode; - } - - /// Gets the internal resize mode exact. - /// # JS-WASM - /// This is exported as `get_ResizeModeExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = ResizeModeExact) - )] - pub fn resize_mode_exact(&self) -> bool { - self.resize_mode_exact - } - - /// Sets the internal resize mode exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_ResizeModeExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = ResizeModeExact) - )] - pub fn set_resize_mode_exact(&mut self, resize_mode_exact: bool) { - self.resize_mode_exact = resize_mode_exact; - } - - /// Gets the internal device id. - /// # JS-WASM - /// This is exported as `get_DeviceId`. - #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = DeviceId))] - pub fn device_id(&self) -> String { - self.device_id.to_string() - } - - /// Sets the internal device ID. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_DeviceId`. - #[cfg_attr(feature = "output-wasm", wasm_bindgen(setter = DeviceId))] - pub fn set_device_id(&mut self, device_id: String) { - self.device_id = device_id; - } - - /// Gets the internal device id exact. - /// # JS-WASM - /// This is exported as `get_DeviceIdExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = DeviceIdExact) - )] - pub fn device_id_exact(&self) -> bool { - self.device_id_exact - } - - /// Sets the internal device ID exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_DeviceIdExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = DeviceIdExact) - )] - pub fn set_device_id_exact(&mut self, device_id_exact: bool) { - self.device_id_exact = device_id_exact; - } - - /// Gets the internal group id. - /// # JS-WASM - /// This is exported as `get_GroupId`. - #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = GroupId))] - pub fn group_id(&self) -> String { - self.group_id.to_string() - } - - /// Sets the internal group ID. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_GroupId`. - #[cfg_attr(feature = "output-wasm", wasm_bindgen(setter = GroupId))] - pub fn set_group_id(&mut self, group_id: String) { - self.group_id = group_id; - } - - /// Gets the internal group id exact. - /// # JS-WASM - /// This is exported as `get_GroupIdExact`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = GroupIdExact) - )] - pub fn group_id_exact(&self) -> bool { - self.group_id_exact - } - - /// Sets the internal group ID exact. - /// Note that this doesn't affect the internal [`MediaStreamConstraints`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStreamConstraints.html) until you call - /// [`apply_constraints()`](crate::js_camera::JSCameraConstraints::apply_constraints) - /// # JS-WASM - /// This is exported as `set_GroupIdExact`. - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = GroupIdExact) - )] - pub fn set_group_id_exact(&mut self, group_id_exact: bool) { - self.group_id_exact = group_id_exact; - } - - /// Applies any modified constraints. - /// # JS-WASM - /// This is exported as `applyConstraints`. - #[cfg(feature = "output-wasm")] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = applyConstraints))] - pub fn js_apply_constraints(&mut self) { - self.apply_constraints(); - } -} - -impl JSCameraConstraints { - /// Applies any modified constraints. - pub fn apply_constraints(&mut self) { - let new_constraints = JSCameraConstraintsBuilder { - min_resolution: self.min_resolution(), - preferred_resolution: self.resolution(), - max_resolution: self.max_resolution(), - resolution_exact: self.resolution_exact(), - min_aspect_ratio: self.min_aspect_ratio(), - aspect_ratio: self.aspect_ratio(), - max_aspect_ratio: self.max_aspect_ratio(), - aspect_ratio_exact: self.aspect_ratio_exact(), - facing_mode: self.facing_mode(), - facing_mode_exact: self.facing_mode_exact(), - min_frame_rate: self.min_frame_rate(), - frame_rate: self.frame_rate(), - max_frame_rate: self.max_frame_rate(), - frame_rate_exact: self.frame_rate_exact(), - resize_mode: self.resize_mode(), - resize_mode_exact: self.resize_mode_exact(), - device_id: self.device_id(), - device_id_exact: self.device_id_exact(), - group_id: self.group_id(), - group_id_exact: self.group_id_exact(), - } - .build(); - - self.media_constraints = new_constraints.media_constraints; - } -} - -impl Deref for JSCameraConstraints { - type Target = MediaStreamConstraints; - - fn deref(&self) -> &Self::Target { - &self.media_constraints - } -} - -/// A wrapper around a [`MediaStream`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.MediaStream.html) -/// # JS-WASM -/// This is exported as `NokhwaCamera`. -#[cfg(feature = "input-jscam")] -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = NokhwaCamera))] -#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-jscam")))] -pub struct JSCamera { - media_stream: MediaStream, - constraints: JSCameraConstraints, - attached: bool, - attached_node: Option, - measured_resolution: Resolution, - attached_canvas: Option, - canvas_context: Option, -} - -#[cfg(feature = "input-jscam")] -#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_class = NokhwaCamera))] -impl JSCamera { - /// Creates a new [`JSCamera`] using [`JSCameraConstraints`]. - /// - /// # Errors - /// This may error if permission is not granted, or the constraints are invalid. - /// # JS-WASM - /// This is the constructor for `NokhwaCamera`. It returns a promise and may throw an error. - #[cfg(feature = "output-wasm")] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(constructor))] - pub async fn js_new(constraints: JSCameraConstraints) -> Result { - match JSCamera::new(constraints).await { - Ok(camera) => Ok(camera), - Err(why) => Err(JsValue::from(why.to_string())), - } - } - - /// Gets the internal [`JSCameraConstraints`]. - /// Most likely, you will edit this value by taking ownership of it, then feed it back into [`set_constraints`](crate::js_camera::JSCamera::set_constraints). - /// # JS-WASM - /// This is exported as `get_Constraints`. - #[must_use] - #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Constraints))] - pub fn constraints(&self) -> JSCameraConstraints { - self.constraints.clone() - } - - /// Sets the [`JSCameraConstraints`]. This calls [`apply_constraints`](crate::js_camera::JSCamera::apply_constraints) internally. - /// - /// # Errors - /// See [`apply_constraints`](crate::js_camera::JSCamera::apply_constraints). - /// # JS-WASM - /// This is exported as `set_Constraints`. It may throw an error. - #[cfg(feature = "output-wasm")] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(setter = Constraints) - )] - pub fn js_set_constraints(&mut self, constraints: JSCameraConstraints) -> Result<(), JsValue> { - match self.set_constraints(constraints) { - Ok(_) => Ok(()), - Err(why) => Err(JsValue::from(why.to_string())), - } - } - - /// Gets the internal [`Resolution`]. - /// - /// Note: This value is only updated after you call [`measure_resolution`](crate::js_camera::JSCamera::measure_resolution) - /// # JS-WASM - /// This is exported as `get_Resolution`. - #[must_use] - #[cfg_attr( - feature = "output-wasm", - wasm_bindgen(getter = Resolution) - )] - pub fn resolution(&self) -> Resolution { - self.measured_resolution - } - - /// Measures the [`Resolution`] of the internal stream. You usually do not need to call this. - /// - /// # Errors - /// If the camera fails to attach to the created `