diff --git a/Cargo.toml b/Cargo.toml index f8e2962..77b9081 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ members = [ "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", - "examples/*", ] exclude = [ "examples/jscam" ] @@ -46,29 +45,27 @@ input-opencv = [ "opencv/clang-runtime", "nokhwa-core/opencv-mat", ] -input-jscam = [ - "web-sys", - "js-sys", - "wasm-bindgen-futures", - "wasm-bindgen", - "wasm-rs-async-executor", -] +# input-jscam = [ +# "web-sys", +# "js-sys", +# "wasm-bindgen-futures", +# "wasm-bindgen", +# "wasm-rs-async-executor", +# ] output-wgpu = [ "wgpu", "nokhwa-core/wgpu-types" ] -#output-wasm = ["input-jscam"] -output-threaded = [ "parking_lot", "camera-sync-impl" ] +# output-wasm = [ "input-jscam" ] +output-threaded = [ "parking_lot", "camera-sync-impl" ] camera-sync-impl = [ ] -small-wasm = [ ] +# small-wasm = [ ] docs-only = [ "input-native", "input-opencv", - "input-jscam", "output-wgpu", "output-threaded", "serialize", ] docs-nolink = [ "nokhwa-core/docs-features" ] docs-features = [ ] -test-fail-warning = [ ] [dependencies] thiserror = "2.0" diff --git a/flake.nix b/flake.nix index e8b6934..c2007b8 100644 --- a/flake.nix +++ b/flake.nix @@ -5,50 +5,60 @@ rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - overlays = [ (import rust-overlay) ]; + outputs = { + self, + nixpkgs, + rust-overlay, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { - inherit system overlays; + inherit system; + overlays = [rust-overlay.overlays.default]; }; rustbin = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { - extensions = ["rust-src" "clippy" "rustfmt" "miri"]; - }); - in - { - devShells.default = pkgs.mkShell { - #LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - #BINDGEN_EXTRA_CLANG_ARGS = "-isystem ${pkgs.libclang.lib}/lib/clang/${flake-utils.lib.getVersion pkgs.clang}/include"; - - nativeBuildInputs = [ - pkgs.pkg-config - pkgs.cmake - pkgs.vcpkg - ]; - packages = with pkgs; [ - rust-analyzer - pkg-config - opencv - alsa-lib - systemdLibs - cmake - fontconfig - linuxHeaders - rustPlatform.bindgenHook - llvmPackages.libclang.lib - llvmPackages.clang - libv4l - v4l-utils - rustbin + extensions = [ + "rust-src" + "clippy" + "rustfmt" + "miri" + "rust-analyzer" ]; - LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - shellHook = '' - export LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - cargo version - ''; + }); + in { + formatter = pkgs.alejandra; + devShells.default = pkgs.mkShell { + packages = + [ + rustbin + ] + ++ (with pkgs; [ + rust-analyzer + pkg-config + opencv + alsa-lib + systemdLibs + cmake + fontconfig + linuxHeaders + rustPlatform.bindgenHook + llvmPackages.libclang.lib + llvmPackages.clang + libv4l + v4l-utils + rustbin + ]); + + env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library"; + env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + + shellHook = '' + echo "WONDERHOOOOOY!!!!" + ''; }; } ); diff --git a/nokhwa-bindings-linux/Cargo.toml b/nokhwa-bindings-linux/Cargo.toml index a78f9b4..35b6ba8 100644 --- a/nokhwa-bindings-linux/Cargo.toml +++ b/nokhwa-bindings-linux/Cargo.toml @@ -1,23 +1,23 @@ [package] -name = "nokhwa-bindings-linux" -version = "0.1.2" -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/l1npengtul/nokhwa" +name = "nokhwa-bindings-linux" +version = "0.1.3" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/l1npengtul/nokhwa" description = "The V4L2 bindings crate for `nokhwa`" -keywords = ["v4l", "v4l2", "linux", "capture", "webcam"] +keywords = [ "v4l", "v4l2", "linux", "capture", "webcam" ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] # Disable arena buffer of v4l2 for the crate to work on raspberry pi with faulty v4l2 driver # https://github.com/l1npengtul/nokhwa/pull/121 -no-arena-buffer = [] +no-arena-buffer = [ ] [dependencies] [dependencies.nokhwa-core] version = "0.1" -path = "../nokhwa-core" +path = "../nokhwa-core" [target.'cfg(target_os="linux")'.dependencies] -v4l = { version = "0.14", features = ["v4l2-sys"] } +v4l = { version = "0.14", features = [ "v4l2-sys" ] } diff --git a/nokhwa-bindings-linux/src/lib.rs b/nokhwa-bindings-linux/src/lib.rs index a8dcc94..acc2a65 100644 --- a/nokhwa-bindings-linux/src/lib.rs +++ b/nokhwa-bindings-linux/src/lib.rs @@ -41,7 +41,7 @@ mod internal { control::{Control, Flags, Type, Value}, frameinterval::FrameIntervalEnum, framesize::FrameSizeEnum, - io::traits::CaptureStream, + io::traits::{CaptureStream, Stream}, prelude::MmapStream, video::{capture::Parameters, Capture}, Device, Format, FourCC, @@ -885,7 +885,7 @@ mod internal { )) } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { match &mut self.stream_handle { Some(sh) => match sh.next() { Ok((data, _)) => Ok(Cow::Borrowed(data)), @@ -1090,7 +1090,7 @@ mod internal { todo!() } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { todo!() } diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index 8b3edb0..5ac3564 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -1129,7 +1129,7 @@ pub mod wmf { Ok(()) } - pub fn raw_bytes(&mut self) -> Result, NokhwaError> { + pub fn raw_bytes(&mut self) -> Result, NokhwaError> { let mut imf_sample: Option = match unsafe { MFCreateSample() } { Ok(sample) => Some(sample), Err(why) => { @@ -1333,7 +1333,7 @@ pub mod wmf { )) } - pub fn raw_bytes(&mut self) -> Result, NokhwaError> { + pub fn raw_bytes(&mut self) -> Result, NokhwaError> { Err(NokhwaError::NotImplementedError( "Only on Windows".to_string(), )) diff --git a/nokhwa-core/src/buffer.rs b/nokhwa-core/src/buffer.rs index ce1f1cd..5ff5098 100644 --- a/nokhwa-core/src/buffer.rs +++ b/nokhwa-core/src/buffer.rs @@ -117,7 +117,9 @@ impl Buffer { /// 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")))] - pub fn decode_opencv_mat(&mut self) -> Result, NokhwaError> { + pub fn decode_opencv_mat( + &mut self, + ) -> Result, NokhwaError> { use crate::buffer::channel_defs::make_mat; make_mat::(self.resolution, self.buffer()) @@ -230,7 +232,7 @@ pub mod channel_defs { pub(crate) fn make_mat( resolution: Resolution, data: &[u8], - ) -> Result, NokhwaError> + ) -> Result, NokhwaError> where F: FormatDecoder, { diff --git a/nokhwa-core/src/traits.rs b/nokhwa-core/src/traits.rs index 22f1bf3..6ea3e15 100644 --- a/nokhwa-core/src/traits.rs +++ b/nokhwa-core/src/traits.rs @@ -25,7 +25,7 @@ use crate::{ use std::{borrow::Cow, collections::HashMap}; #[cfg(feature = "wgpu-types")] use wgpu::{ - Device as WgpuDevice, Extent3d, ImageCopyTexture, ImageDataLayout, Queue as WgpuQueue, + Device as WgpuDevice, Extent3d, Queue as WgpuQueue, TexelCopyBufferLayout, Texture as WgpuTexture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }; @@ -163,7 +163,7 @@ pub trait CaptureBackendTrait { /// Will get a frame from the camera **without** any processing applied, meaning you will usually get a frame you need to decode yourself. /// # Errors /// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error. - fn frame_raw(&mut self) -> Result, NokhwaError>; + fn frame_raw(&mut self) -> Result, NokhwaError>; /// The minimum buffer size needed to write the current frame. If `alpha` is true, it will instead return the minimum size of the buffer with an alpha channel as well. /// This assumes that you are decoding to RGB/RGBA for [`FrameFormat::MJPEG`] or [`FrameFormat::YUYV`] and Luma8/LumaA8 for [`FrameFormat::GRAY`] @@ -190,12 +190,14 @@ pub trait CaptureBackendTrait { /// 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>( + fn frame_texture( &mut self, device: &WgpuDevice, queue: &WgpuQueue, - label: Option<&'a str>, + label: Option<&str>, ) -> Result { + use wgpu::{Origin3d, TexelCopyTextureInfoBase}; + use crate::pixel_format::RgbAFormat; let frame = self.frame()?.decode_image::()?; @@ -221,14 +223,14 @@ pub trait CaptureBackendTrait { let height_nonzero = frame.height(); queue.write_texture( - ImageCopyTexture { + TexelCopyTextureInfoBase { texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, + mip_level: 1, + origin: Origin3d { x: 0, y: 0, z: 0 }, aspect: TextureAspect::All, }, &frame, - ImageDataLayout { + TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(width_nonzero), rows_per_image: Some(height_nonzero), diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 9725cf9..db7e5a0 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -64,7 +64,7 @@ impl RequestedFormat<'_> { pub fn with_formats( requested: RequestedFormatType, decoder: &[FrameFormat], - ) -> RequestedFormat { + ) -> RequestedFormat<'_> { RequestedFormat { requested_format: requested, wanted_decoder: decoder, diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index d0bf0fc..3c005a4 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -23,14 +23,15 @@ use nokhwa_bindings_macos::{ use nokhwa_core::{ buffer::Buffer, error::NokhwaError, - pixel_format::RgbFormat, traits::CaptureBackendTrait, types::{ ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter, - FrameFormat, KnownCameraControl, RequestedFormat, RequestedFormatType, Resolution, + FrameFormat, KnownCameraControl, RequestedFormat, Resolution, }, }; #[cfg(target_os = "macos")] +use nokhwa_core::{pixel_format::RgbFormat, types::RequestedFormatType}; +#[cfg(target_os = "macos")] use std::{ffi::CString, sync::Arc}; use std::{borrow::Cow, collections::HashMap}; @@ -287,7 +288,7 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { Ok(buffer) } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { let result = match self.frame_buffer_receiver.recv() { Ok(recv) => Ok(Cow::from(recv.0)), Err(why) => Err(NokhwaError::ReadFrameError(why.to_string())), @@ -480,7 +481,7 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { todo!() } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { todo!() } diff --git a/src/backends/capture/msmf_backend.rs b/src/backends/capture/msmf_backend.rs index 0a8597b..352e0e2 100644 --- a/src/backends/capture/msmf_backend.rs +++ b/src/backends/capture/msmf_backend.rs @@ -251,7 +251,7 @@ impl CaptureBackendTrait for MediaFoundationCaptureDevice { )) } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { self.inner.raw_bytes() } diff --git a/src/backends/capture/opencv_backend.rs b/src/backends/capture/opencv_backend.rs index 6cca332..8cbcdc8 100644 --- a/src/backends/capture/opencv_backend.rs +++ b/src/backends/capture/opencv_backend.rs @@ -169,7 +169,7 @@ impl OpenCvCaptureDevice { /// # Errors /// If the frame is failed to be read, this will error. #[allow(clippy::cast_sign_loss)] - pub fn raw_frame_vec(&mut self) -> Result, NokhwaError> { + pub fn raw_frame_vec(&mut self) -> Result, NokhwaError> { if !self.is_stream_open() { return Err(NokhwaError::ReadFrameError( "Stream is not open!".to_string(), @@ -541,7 +541,7 @@ impl CaptureBackendTrait for OpenCvCaptureDevice { )) } - fn frame_raw(&mut self) -> Result, NokhwaError> { + fn frame_raw(&mut self) -> Result, NokhwaError> { let cow = self.raw_frame_vec()?; Ok(cow) } diff --git a/src/camera.rs b/src/camera.rs index 31aca6e..d8be6bf 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -384,7 +384,7 @@ impl Camera { /// Will get a frame from the camera **without** any processing applied, meaning you will usually get a frame you need to decode yourself. /// # Errors /// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error. - pub fn frame_raw(&mut self) -> Result, NokhwaError> { + pub fn frame_raw(&mut self) -> Result, NokhwaError> { match self.device.frame_raw() { Ok(f) => Ok(f), Err(why) => Err(why), diff --git a/src/js_camera.rs b/src/js_camera.rs index 89d892e..ce1024b 100644 --- a/src/js_camera.rs +++ b/src/js_camera.rs @@ -32,7 +32,7 @@ use std::{ fmt::{Debug, Display, Formatter}, ops::Deref, }; -use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; use web_sys::{ console::log_1, CanvasRenderingContext2d, Document, Element, HtmlCanvasElement, @@ -41,8 +41,9 @@ use web_sys::{ }; #[cfg(feature = "output-wgpu")] use wgpu::{ - Device, Extent3d, ImageCopyTexture, ImageDataLayout, Queue, Texture, TextureAspect, - TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, + Device as WgpuDevice, Extent3d, Queue as WgpuQueue, TexelCopyBufferLayout, + Texture as WgpuTexture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, + TextureUsages, }; // why no code completion @@ -203,8 +204,8 @@ pub async fn request_permission() -> Result<(), NokhwaError> { match media_devices.get_user_media_with_constraints( MediaStreamConstraints::new() - .video(&JsValue::from_bool(true)) - .audio(&JsValue::from_bool(false)), + .set_video(&JsValue::from_bool(true)) + .set_audio(&JsValue::from_bool(false)), ) { Ok(promise) => { let js_future = JsFuture::from(promise); @@ -266,8 +267,8 @@ pub async fn query_js_cameras() -> Result, NokhwaError> { if media_device_info.kind() == MediaDeviceKind::Videoinput { match media_devices.get_user_media_with_constraints( MediaStreamConstraints::new() - .audio(&jsv!(false)) - .video(&jsv!(obj!(( + .set_audio(&jsv!(false)) + .set_video(&jsv!(obj!(( "deviceId", media_device_info.device_id() )))), @@ -603,8 +604,8 @@ impl JSCameraConstraintsBuilder { feature = "output-wasm", wasm_bindgen(js_name = MinResolution) )] - pub fn min_resolution(mut self, min_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.min_resolution = Some(min_resolution); + pub fn min_resolution(mut self, min_resolution: &Resolution) -> JSCameraConstraintsBuilder { + self.min_resolution = Some(*min_resolution); self } @@ -618,8 +619,8 @@ impl JSCameraConstraintsBuilder { feature = "output-wasm", wasm_bindgen(js_name = Resolution) )] - pub fn resolution(mut self, new_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.preferred_resolution = new_resolution; + pub fn resolution(mut self, new_resolution: &Resolution) -> JSCameraConstraintsBuilder { + self.preferred_resolution = *new_resolution; self } @@ -633,8 +634,8 @@ impl JSCameraConstraintsBuilder { feature = "output-wasm", wasm_bindgen(js_name = MaxResolution) )] - pub fn max_resolution(mut self, max_resolution: Resolution) -> JSCameraConstraintsBuilder { - self.min_resolution = Some(max_resolution); + pub fn max_resolution(mut self, max_resolution: &Resolution) -> JSCameraConstraintsBuilder { + self.min_resolution = Some(*max_resolution); self } @@ -1038,8 +1039,8 @@ impl JSCameraConstraintsBuilder { } let media_stream_constraints = MediaStreamConstraints::new() - .audio(&jsv!(false)) - .video(&jsv!(video_object)) + .set_audio(&jsv!(false)) + .set_video(&jsv!(video_object)) .clone(); JSCameraConstraints { @@ -2577,20 +2578,20 @@ impl JSCamera { #[cfg(feature = "output-wgpu")] /// 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. - pub fn frame_texture<'a>( + fn frame_texture( &mut self, - device: &Device, - queue: &Queue, - label: Option<&'a str>, - ) -> Result { - use std::num::NonZeroU32; - let resolution = self.resolution(); - let frame = self.frame_raw()?; + device: &WgpuDevice, + queue: &WgpuQueue, + label: Option<&str>, + ) -> Result { + use wgpu::{Origin3d, TexelCopyTextureInfoBase}; + + use crate::pixel_format::RgbAFormat; + let frame = self.frame()?.decode_image::()?; let texture_size = Extent3d { - width: resolution.width(), - height: resolution.height(), + width: frame.width(), + height: frame.height(), depth_or_array_layers: 1, }; @@ -2602,28 +2603,31 @@ impl JSCamera { dimension: TextureDimension::D2, format: TextureFormat::Rgba8UnormSrgb, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - view_formats: &[TextureFormat::Rgba8UnormSrgb], + view_formats: &[], }); + let width_nonzero = 4 * frame.width(); + + let height_nonzero = frame.height(); + queue.write_texture( - ImageCopyTexture { + TexelCopyTextureInfoBase { texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, + mip_level: 1, + origin: Origin3d { x: 0, y: 0, z: 0 }, aspect: TextureAspect::All, }, - frame.borrow(), - ImageDataLayout { + &frame, + TexelCopyBufferLayout { offset: 0, - bytes_per_row: Some(4 * resolution.width()), - rows_per_image: Some(resolution.height()), + bytes_per_row: Some(width_nonzero), + rows_per_image: Some(height_nonzero), }, texture_size, ); Ok(texture) } - /// Checks if the stream is open. pub fn is_open(&self) -> bool { let stream = self diff --git a/src/lib.rs b/src/lib.rs index 2bf60b4..26bbc11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#![cfg_attr(feature = "test-fail-warning", deny(warnings))] #![cfg_attr(feature = "docs-features", feature(doc_cfg))] //! # nokhwa //! A Simple-to-use, cross-platform Rust Webcam Capture Library @@ -34,10 +33,10 @@ pub mod backends; mod camera; mod init; -/// A camera that uses native browser APIs meant for WASM applications. -#[cfg(feature = "input-jscam")] -#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-jscam")))] -pub mod js_camera; +// /// A camera that uses native browser APIs meant for WASM applications. +// #[cfg(feature = "input-jscam")] +// #[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-jscam")))] +// pub mod js_camera; pub use nokhwa_core::pixel_format::FormatDecoder; mod query; diff --git a/src/query.rs b/src/query.rs index d080134..798bbf6 100644 --- a/src/query.rs +++ b/src/query.rs @@ -295,15 +295,14 @@ fn query_avfoundation() -> Result, NokhwaError> { )) } -#[cfg(feature = "input-jscam")] -fn query_wasm() -> Result, NokhwaError> { - use crate::js_camera::query_js_cameras; - use wasm_rs_async_executor::single_threaded::block_on; +// #[cfg(feature = "input-jscam")] +// fn query_wasm() -> Result, NokhwaError> { +// use crate::js_camera::query_js_cameras; +// use wasm_rs_async_executor::single_threaded::block_on; - block_on(query_js_cameras()) -} +// block_on(query_js_cameras()) +// } -#[cfg(not(feature = "input-jscam"))] fn query_wasm() -> Result, NokhwaError> { Err(NokhwaError::UnsupportedOperationError(ApiBackend::Browser)) }