diff --git a/Cargo.toml b/Cargo.toml index 6b7865b..d44b0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,16 +13,16 @@ repository = "https://github.com/l1npengtul/nokhwa" [workspace] members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "nokhwa-decoders"] -exclude = ["examples/*"] [lib] -crate-type = ["cdylib", "rlib"] +crate-type = ["rlib"] [features] -default = ["decoding-yuv","decoding-mozjpeg"] +default = ["decoding-yuv","decoding-mjpeg"] serialize = ["serde", "nokhwa-core/serialize"] -decoding-yuv = ["mozjpeg"] -decoding-mozjpeg = ["mozjpeg"] +decoding-yuv = ["nokhwa-decoders/yuv"] +decoding-mjpeg = ["nokhwa-decoders/mjpeg"] +decoding-ffmpeg = ["nokhwa-decoders/ffmpeg"] input-avfoundation = ["nokhwa-bindings-macos", "flume"] input-msmf = ["nokhwa-bindings-windows"] input-v4l = ["nokhwa-bindings-linux"] @@ -31,7 +31,7 @@ input-native = ["input-avfoundation", "input-v4l", "input-msmf"] # 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", "serde-wasm-bindgen", "serde"] -output-wgpu = ["wgpu", "nokhwa-core/wgpu-types"] +output-wgpu = ["wgpu-types", "nokhwa-core/wgpu"] #output-wasm = ["input-jscam"] output-threaded = [] output-async = ["nokhwa-core/async", "async-trait"] @@ -43,44 +43,10 @@ test-fail-warning = [] [dependencies] paste = "1.0" -[dependencies.mozjpeg] -version = "0.10" -optional = true - [dependencies.nokhwa-core] version = "0.2" path = "nokhwa-core" -[dependencies.serde] -version = "1.0" -optional = true - -[dependencies.flume] -version = "0.11" -optional = true - -[dependencies.image] -version = "0.25" -default-features = false - -[dependencies.usb_enumeration] -version = "0.2" -optional = true - -[dependencies.wgpu] -version = "25" -optional = true - -[dependencies.opencv] -version = "0.94" -default-features = false -features = ["videoio"] -optional = true - -[dependencies.rgb] -version = "0.8" -optional = true - [dependencies.nokhwa-bindings-windows] version = "0.4" path = "nokhwa-bindings-windows" @@ -96,6 +62,37 @@ version = "0.2" path = "nokhwa-bindings-linux" optional = true +[dependencies.nokhwa-decoders] +path = "nokhwa-decoders" +optional = true + +[dependencies.serde] +workspace = true +optional = true + +[dependencies.flume] +version = "0.11" +optional = true + +[dependencies.image] +workspace = true + +[dependencies.usb_enumeration] +version = "0.2" +optional = true + +[dependencies.wgpu-types] +workspace = true +optional = true + +[dependencies.opencv] +workspace = true +optional = true + +[dependencies.rgb] +version = "0.8" +optional = true + [dependencies.web-sys] version = "0.3" features = [ @@ -144,3 +141,18 @@ optional = true [package.metadata.docs.rs] features = ["docs-only", "docs-nolink", "docs-features"] + +[workspace.dependencies.image] +version = "0.25" +default-features = false + +[workspace.dependencies.serde] +version = "1.0" +features = ["derive"] + +[workspace.dependencies.wgpu-types] +version = "26" + +[workspace.dependencies.opencv] +version = "0.95" +default-features = false diff --git a/flake.lock b/flake.lock index b2d784f..d412d80 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751498133, - "narHash": "sha256-QWJ+NQbMU+NcU2xiyo7SNox1fAuwksGlQhpzBl76g1I=", + "lastModified": 1755020227, + "narHash": "sha256-gGmm+h0t6rY88RPTaIm3su95QvQIVjAJx558YUG4Id8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d55716bb59b91ae9d1ced4b1ccdea7a442ecbfdb", + "rev": "695d5db1b8b20b73292501683a524e0bd79074fb", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1751510438, - "narHash": "sha256-m8PjOoyyCR4nhqtHEBP1tB/jF+gJYYguSZmUmVTEAQE=", + "lastModified": 1755052812, + "narHash": "sha256-Tjw2YP7Hz8+ibE8wJ+Ps65vh1lzAe5ozmoo9sdQ7rGg=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "7f415261f298656f8164bd636c0dc05af4e95b6b", + "rev": "433023cba5f4fa66b8b0fdbb8f91d420c9cc2527", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6c7f9fc..01f3d8f 100644 --- a/flake.nix +++ b/flake.nix @@ -5,57 +5,97 @@ rust-overlay.url = "github:oxalica/rust-overlay"; }; - outputs = { - self, - nixpkgs, - rust-overlay, - flake-utils, - ... - }: + outputs = + { + self, + nixpkgs, + rust-overlay, + flake-utils, + ... + }: flake-utils.lib.eachDefaultSystem ( - system: let + system: + let pkgs = import nixpkgs { inherit system; - overlays = [rust-overlay.overlays.default]; + overlays = [ rust-overlay.overlays.default ]; + config.allowUnfree = true; }; - rustbin = pkgs.rust-bin.selectLatestNightlyWith (toolchain: + rustshell = pkgs.mkShell.override { + stdenv = pkgs.gccStdenv; + }; + rustbin = pkgs.rust-bin.selectLatestNightlyWith ( + toolchain: toolchain.default.override { - extensions = ["rust-src" "clippy" "rustfmt" "miri"]; - }); - in { + extensions = [ + "rust-src" + "clippy" + "rustfmt" + "miri" + "rust-analyzer" + ]; + } + ); + in + { formatter = pkgs.alejandra; - devShells.default = pkgs.mkShell { + devShells.default = rustshell { packages = [ rustbin - ] ++ (with pkgs; [ - llvmPackages.libclang.lib - llvmPackages.clang - pkg-config - cmake - vcpkg - rustPlatform.bindgenHook - xmlstarlet - opencv - alsa-lib - systemdLibs - cmake - fontconfig - linuxHeaders - v4l-utils - libv4l - pipewire - rustup - ffmpeg-full - nasm + ] + ++ (with pkgs; [ + llvmPackages_21.clangWithLibcAndBasicRtAndLibcxx + pkg-config + cmake + vcpkg + lldb + rustPlatform.bindgenHook + xmlstarlet + opencv + alsa-lib + systemdLibs + cmake + fontconfig + linuxHeaders + v4l-utils + libv4l + pipewire + rustup + gcc + ffmpeg-full + nasm + libGL + flite + quirc + lcevcdec + xz + celt + opencore-amr + snappy + codec2 + gsm + ilbc + lame + libtheora + libogg + twolame + vo-amrwbenc + vvenc + xavs + xvidcore + soxr + libvdpau + jetbrains.rust-rover ]); env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library"; env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - shellHook = let - pathToRustProject = "/project/component[@name='RustProjectSettings']"; - in + shellHook = + let + pathToRustProject = "/project/component[@name='RustProjectSettings']"; + in '' echo "WONDERHOOOOOY!!!!" xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='explicitPathToStdlib']/@value" --value "${rustbin}/lib/rustlib/src/rust/library" .idea/workspace.xml diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index 831f1f7..af5f503 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -29,7 +29,6 @@ typed-builder = "0.21" compact_str = "0.9" bytemuck = "1.23" smallmap = "1.4" -constcat = "0.6" paste = "1.0" [dependencies.num-rational] @@ -38,21 +37,18 @@ default-features = false features = ["serde", "std"] [dependencies.image] -version = "0.25" -default-features = false +workspace = true [dependencies.serde] -version = "1.0" -features = ["derive"] +workspace = true optional = true [dependencies.wgpu-types] -version = "25" +workspace = true optional = true [dependencies.opencv] -version = "0.94" -default-features = false +workspace = true optional = true [dependencies.async-trait] diff --git a/nokhwa-core/src/decoder.rs b/nokhwa-core/src/decoder.rs index e82f705..8870d61 100644 --- a/nokhwa-core/src/decoder.rs +++ b/nokhwa-core/src/decoder.rs @@ -1,7 +1,7 @@ use crate::error::NokhwaError; use crate::frame_buffer::FrameBuffer; use crate::image::{DecodedImage, NonFloatScalarWidth}; -use crate::types::{CameraFormat, Resolution}; +use crate::types::{Resolution}; pub use image::{ImageBuffer, Pixel, Primitive}; use std::fmt::Debug; diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index 3211231..ad9c327 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -14,9 +14,9 @@ * limitations under the License. */ use crate::frame_format::FrameFormat; -use crate::platform::Backends; use std::fmt::Debug; use thiserror::Error; +use crate::types::Backends; pub type NokhwaResult = Result; diff --git a/nokhwa-core/src/image.rs b/nokhwa-core/src/image.rs index 2802448..dd03632 100644 --- a/nokhwa-core/src/image.rs +++ b/nokhwa-core/src/image.rs @@ -1,5 +1,5 @@ use bytemuck::Pod; -use image::{ImageBuffer, Pixel, Primitive}; +pub use image::{ImageBuffer, Pixel, Primitive}; use num_traits::{NumCast, PrimInt}; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index 84ca4ce..09fed97 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -1,23 +1,8 @@ use crate::camera::Camera; use crate::error::NokhwaResult; -use crate::types::{CameraIndex, CameraInformation}; +use crate::types::{Backends, CameraIndex, CameraInformation, QueriedCamera}; use std::fmt::{Display, Formatter}; -#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] -pub enum Backends { - Video4Linux2, - WebWASM, - AVFoundation, - MicrosoftMediaFoundation, - OpenCV, - Custom(&'static str), -} - -impl Display for Backends { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} pub trait PlatformTrait { const PLATFORM: Backends; @@ -27,7 +12,7 @@ pub trait PlatformTrait { fn check_permission_given(&mut self) -> bool; - fn query(&mut self) -> NokhwaResult>; + fn query(&mut self) -> NokhwaResult>; fn open(&mut self, index: CameraIndex) -> NokhwaResult; @@ -47,7 +32,7 @@ pub trait AsyncPlatformTrait: PlatformTrait { async fn await_permission(&mut self) -> NokhwaResult<()>; - async fn query_async(&mut self) -> NokhwaResult>; + async fn query_async(&mut self) -> NokhwaResult>; async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult; diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 033c210..868cc22 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -44,9 +44,8 @@ impl CameraIndex { pub fn as_string(&self) -> String { match self { CameraIndex::Index(i) => i.to_string(), - CameraIndex::String(s) => s.to_string(), - CameraIndex::Stable(s) => s.to_string(), - } + CameraIndex::String(s) | CameraIndex::Stable(s) => s.to_string(), + } } /// Returns true if this [`CameraIndex`] contains an [`CameraIndex::Index`] @@ -133,6 +132,7 @@ impl Resolution { /// Get the x (width) of Resolution #[must_use] #[inline] + #[deprecated] pub fn x(self) -> u32 { self.width } @@ -140,6 +140,7 @@ impl Resolution { /// Get the y (height) of Resolution #[must_use] #[inline] + #[deprecated] pub fn y(self) -> u32 { self.height } @@ -152,7 +153,7 @@ impl Resolution { impl Display for Resolution { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}x{}", self.x(), self.y()) + write!(f, "{}x{}", self.width(), self.height()) } } @@ -164,9 +165,9 @@ impl PartialOrd for Resolution { impl Ord for Resolution { fn cmp(&self, other: &Self) -> Ordering { - match self.x().cmp(&other.x()) { + match self.width().cmp(&other.width()) { Ordering::Less => Ordering::Less, - Ordering::Equal => self.y().cmp(&other.y()), + Ordering::Equal => self.height().cmp(&other.height()), Ordering::Greater => Ordering::Greater, } } @@ -174,11 +175,11 @@ impl Ord for Resolution { impl Distance for Resolution { fn distance_from(&self, other: &Self) -> u32 { - let x1 = self.x(); - let x2 = other.x(); + let x1 = self.width(); + let x2 = other.width(); - let y1 = self.y(); - let y2 = other.y(); + let y1 = self.height(); + let y2 = other.height(); (x2 - x1).pow(2) + (y2 - y1).pow(2) } @@ -188,8 +189,8 @@ impl Div for Resolution { type Output = Resolution; fn div(self, rhs: Self) -> Self::Output { - let x_div = self.x().div(rhs.x()); - let y_div = self.y().div(rhs.y()); + let x_div = self.width().div(rhs.width()); + let y_div = self.height().div(rhs.height()); Resolution::new(x_div, y_div) } } @@ -198,8 +199,8 @@ impl Sub for Resolution { type Output = Resolution; fn sub(self, rhs: Self) -> Self::Output { - let x_sub = self.x().sub(rhs.x()); - let y_sub = self.y().sub(rhs.y()); + let x_sub = self.width().sub(rhs.width()); + let y_sub = self.height().sub(rhs.height()); Resolution::new(x_sub, y_sub) } } @@ -208,8 +209,8 @@ impl Rem for Resolution { type Output = Resolution; fn rem(self, rhs: Self) -> Self::Output { - let x_rem = self.x().rem(rhs.x()); - let y_rem = self.y().rem(rhs.y()); + let x_rem = self.width().rem(rhs.width()); + let y_rem = self.height().rem(rhs.height()); Resolution::new(x_rem, y_rem) } } @@ -429,15 +430,15 @@ impl Display for CameraFormat { } /// Information about a Camera e.g. its name. -/// `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. +/// `description` and `misc` may contain information that may differ from backend to backend. Refer to each backend for details. +/// `stable_id` contains the stable ID that may be used to reopen the same device. #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct CameraInformation { human_name: String, description: String, misc: String, - index: CameraIndex, + stable_id: Option, } impl CameraInformation { @@ -446,12 +447,12 @@ impl CameraInformation { // 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. - pub fn new(human_name: String, description: String, misc: String, index: CameraIndex) -> Self { + pub fn new(human_name: String, description: String, misc: String, stable_id: Option) -> Self { CameraInformation { human_name, description, misc, - index, + stable_id, } } @@ -459,146 +460,55 @@ impl CameraInformation { #[must_use] // yes, i know, unnecessary alloc this, unnecessary alloc that // but wasm bindgen - pub fn human_name(&self) -> String { - self.human_name.clone() - } - - /// Set the device info's human name. - /// # JS-WASM - /// This is exported as a `set_HumanReadableName`. - pub fn set_human_name(&mut self, human_name: &str) { - self.human_name = human_name.to_string(); + pub fn human_name(&self) -> &str { + &self.human_name } /// Get a reference to the device info's description. - /// # JS-WASM - /// This is exported as a `get_Description`. #[must_use] pub fn description(&self) -> &str { - self.description.borrow() - } - - /// Set the device info's description. - /// # JS-WASM - /// This is exported as a `set_Description`. - pub fn set_description(&mut self, description: &str) { - self.description = description.to_string(); + &self.description } /// Get a reference to the device info's misc. - /// # JS-WASM - /// This is exported as a `get_MiscString`. #[must_use] - pub fn misc(&self) -> String { - self.misc.clone() + pub fn misc(&self) -> &str { + &self.misc } - /// Set the device info's misc. - /// # JS-WASM - /// This is exported as a `set_MiscString`. - pub fn set_misc(&mut self, misc: &str) { - self.misc = misc.to_string(); + #[must_use] pub fn stable_id(&self) -> Option<&str> { + self.stable_id.as_deref() } - - /// Get a reference to the device info's index. - /// # JS-WASM - /// This is exported as a `get_Index`. - #[must_use] - pub fn index(&self) -> &CameraIndex { - &self.index - } - - /// Set the device info's index. - /// # JS-WASM - /// This is exported as a `set_Index`. - pub fn set_index(&mut self, index: CameraIndex) { - self.index = index; - } - - // /// Gets the device info's index as an `u32`. - // /// # Errors - // /// If the index is not parsable as a `u32`, this will error. - // /// # JS-WASM - // /// This is exported as `get_Index_Int` - // #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Index_Int))] - // pub fn index_num(&self) -> Result { - // match &self.index { - // CameraIndex::Index(i) => Ok(*i), - // CameraIndex::String(s) => match s.parse::() { - // Ok(p) => Ok(p), - // Err(why) => Err(NokhwaError::GetPropertyError { - // property: "index-int".to_string(), - // error: why.to_string(), - // }), - // }, - // } - // } } impl Display for CameraInformation { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "Name: {}, Description: {}, Extra: {}, Index: {}", - self.human_name, self.description, self.misc, self.index + "Name: {}, Description: {}, Extra: {}, Stable Index: {:?}", + self.human_name, self.description, self.misc, self.stable_id ) } } -// fn step_chk(val: i64, default: i64, step: i64) -> Result<(), NokhwaError> { -// if (val - default) % step != 0 { -// return Err(NokhwaError::StructureError { -// structure: "Value".to_string(), -// error: "Doesnt fit step".to_string(), -// }); -// } -// Ok(()) -// } +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] +pub enum Backends { + Video4Linux2, + WebWASM, + AVFoundation, + MicrosoftMediaFoundation, + OpenCV, + Custom(&'static str), +} -// /// The list of known capture backends to the library.
-// /// - `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. -// /// - `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. -// #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -// #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -// pub enum ApiBackend { -// Custom(&'static str), -// AVFoundation, -// Video4Linux, -// UniversalVideoClass, -// MediaFoundation, -// OpenCv, -// GStreamer, -// Browser, -// } -// -// impl Display for ApiBackend { -// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { -// write!(f, "{self:?}") -// } -// } +impl Display for Backends { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +#[derive(Clone, Debug, PartialOrd, PartialEq)] +pub struct QueriedCamera { + pub index: CameraIndex, + pub information: CameraInformation +} diff --git a/nokhwa-decoders/Cargo.toml b/nokhwa-decoders/Cargo.toml index 6910b61..d74fc18 100644 --- a/nokhwa-decoders/Cargo.toml +++ b/nokhwa-decoders/Cargo.toml @@ -5,10 +5,9 @@ edition = "2024" [features] ffmpeg = ["ffmpeg-the-third"] -av1 = ["rav1e"] yuyv = ["dcv-color-primitives", "yuv"] mjpeg = ["zune-jpeg", "zune-core"] -static = ["ffmpeg-the-third/static"] +#static = ["ffmpeg-the-third/static"] async = [] [dependencies] @@ -22,10 +21,6 @@ path = "../nokhwa-core" version = "3.0" optional = true -[dependencies.rav1e] -version = "0.8" -optional = true - [dependencies.yuv] version = "0.8" optional = true @@ -41,3 +36,7 @@ optional = true [dependencies.zune-core] version = "0.5.0-rc2" optional = true + +[dev-dependencies.image] +workspace = true +features = ["png", "jpeg", "avif"] diff --git a/nokhwa-decoders/src/ffmpeg.rs b/nokhwa-decoders/src/ffmpeg.rs index fc672bd..655405d 100644 --- a/nokhwa-decoders/src/ffmpeg.rs +++ b/nokhwa-decoders/src/ffmpeg.rs @@ -4,11 +4,10 @@ use ffmpeg_the_third::codec::{Context, Id, Parameters}; use ffmpeg_the_third::decoder::Video; use ffmpeg_the_third::ffi::{ AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace, - AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, - AVPixelFormat, AVRational, SwsContext, av_frame_alloc, av_frame_move_ref, - av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context, - avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext, - sws_scale_frame, + AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, AVPixelFormat, AVRational, + SwsContext, av_frame_alloc, av_frame_move_ref, av_image_copy_to_buffer, av_image_fill_arrays, + av_image_get_buffer_size, avcodec_free_context, avcodec_parameters_alloc, + avcodec_parameters_free, sws_freeContext, sws_getContext, sws_scale_frame, }; use ffmpeg_the_third::packet::{Borrow, Ref}; use ffmpeg_the_third::{Frame, decoder, packet::Packet}; @@ -72,7 +71,7 @@ impl Decoder for FfmpegDecoder { &mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, - _destination_format: Option + _destination_format: Option, ) -> Result { // TODO: add an extra zippy happy path for rgb/bgr/luma let (frame, metadata) = self.receive_decoded_frame(to_decode)?; @@ -108,19 +107,20 @@ impl Decoder for FfmpegDecoder { where

::Subpixel: NonFloatScalarWidth, { - let destination_format = - pixel_to_destination_px_fmt::

() - .ok_or(NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()))?; + let destination_format = pixel_to_destination_px_fmt::

().ok_or( + NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()), + )?; let buffer = buffer.as_mut(); - let estimated_size = - self.codec - .preferred_buffer_min_size(&None)? - .ok_or(NokhwaError::DecoderInvalidBuffer( - "failed to estimate decoder buffer.length".to_string(), - ))?; + let estimated_size = self.codec.preferred_buffer_min_size(&None)?.ok_or( + NokhwaError::DecoderInvalidBuffer( + "failed to estimate decoder buffer.length".to_string(), + ), + )?; if buffer.len() < estimated_size { - return Err(NokhwaError::DecoderInvalidBuffer("buffer too small!".to_string())); + return Err(NokhwaError::DecoderInvalidBuffer( + "buffer too small!".to_string(), + )); } let (mut frame, decoded_meta) = self.receive_decoded_frame(to_decode)?; @@ -198,7 +198,12 @@ impl Decoder for FfmpegDecoder { { let min_size = self.output_decoder_min_size_pixel::

(self.config().resolution); let mut buffer: Vec = vec![::DEFAULT_MIN_VALUE; min_size]; - let meta = self.decode_to_buffer(to_decode, try_cast_slice_mut(&mut buffer).map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?, None)?; + let meta = self.decode_to_buffer( + to_decode, + try_cast_slice_mut(&mut buffer) + .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?, + None, + )?; Ok(DecodedImage::new( ImageBuffer::from_vec( self.codec.config.resolution.width(), @@ -212,9 +217,19 @@ impl Decoder for FfmpegDecoder { )) } - fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { - let size = - unsafe { av_image_get_buffer_size(destination_format, resolution.width() as i32, resolution.height() as i32, 1) }; + fn output_decoder_min_size( + &self, + resolution: Resolution, + destination_format: Self::DestinationFormatHint, + ) -> usize { + let size = unsafe { + av_image_get_buffer_size( + destination_format, + resolution.width() as i32, + resolution.height() as i32, + 1, + ) + }; size as usize } @@ -353,11 +368,13 @@ pub struct FfmpegCodec { impl FfmpegCodec { fn new(config: ::Config) -> Result { - let id = convert_format_to_codec_id(&config.frame_format) - .ok_or(NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format))?; + let id = convert_format_to_codec_id(&config.frame_format).ok_or( + NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format), + )?; - let codec = - decoder::find(id).ok_or(NokhwaError::DecoderInitializationError("Failed to find codec".to_string()))?; + let codec = decoder::find(id).ok_or(NokhwaError::DecoderInitializationError( + "Failed to find codec".to_string(), + ))?; let context = unsafe { let ptr = ffmpeg_the_third::ffi::avcodec_alloc_context3(codec.as_ptr()); @@ -375,9 +392,11 @@ impl FfmpegCodec { .map_err(|why| NokhwaError::Decoder(why.to_string()))?; video .set_parameters(unsafe { - Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::DecoderInitializationError( - "Failed to convert parameters".to_string(), - ))? + Parameters::from_raw(config.as_ptr()?).ok_or( + NokhwaError::DecoderInitializationError( + "Failed to convert parameters".to_string(), + ), + )? }) .map_err(|why| NokhwaError::Decoder(why.to_string()))?; @@ -416,9 +435,11 @@ impl Codec for FfmpegCodec { let mut temp_config = config.as_avcodec_params()?; self.decoder .set_parameters(unsafe { - Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::DecoderInvalidConfiguration( - "Failed to convert parameters".to_string(), - ))? + Parameters::from_raw(&mut temp_config).ok_or( + NokhwaError::DecoderInvalidConfiguration( + "Failed to convert parameters".to_string(), + ), + )? }) .map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?; self.config = config; diff --git a/nokhwa-decoders/src/lib.rs b/nokhwa-decoders/src/lib.rs index 8248bff..26d5d95 100644 --- a/nokhwa-decoders/src/lib.rs +++ b/nokhwa-decoders/src/lib.rs @@ -1,3 +1,5 @@ +extern crate core; + #[cfg(feature = "ffmpeg")] pub mod ffmpeg; #[cfg(feature = "mjpeg")] diff --git a/nokhwa-decoders/src/mjpeg.rs b/nokhwa-decoders/src/mjpeg.rs index 09aaad2..92fe35c 100644 --- a/nokhwa-decoders/src/mjpeg.rs +++ b/nokhwa-decoders/src/mjpeg.rs @@ -1,15 +1,16 @@ -use zune_core::bytestream::ZCursor; -use zune_core::colorspace::ColorSpace; -pub use zune_core::options::DecoderOptions; -use zune_jpeg::errors::DecodeErrors; -pub use zune_jpeg::ImageInfo; -use zune_jpeg::JpegDecoder; -use nokhwa_core::codec::Codec; +use bytemuck::cast_slice_mut; use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel}; use nokhwa_core::error::NokhwaError; use nokhwa_core::frame_buffer::FrameBuffer; +use nokhwa_core::image::Primitive; use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth}; use nokhwa_core::types::Resolution; +use zune_core::bytestream::ZCursor; +use zune_core::colorspace::ColorSpace; +pub use zune_core::options::DecoderOptions; +pub use zune_jpeg::ImageInfo; +use zune_jpeg::JpegDecoder; +use zune_jpeg::errors::DecodeErrors; #[derive(Clone, Debug)] pub struct MJpegDecoder { @@ -30,7 +31,12 @@ impl Decoder for MJpegDecoder { Ok(()) } - fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option) -> Result { + fn decode_to_buffer( + &mut self, + to_decode: FrameBuffer, + mut buffer: impl AsMut<[u8]>, + destination_format_hint: Option, + ) -> Result { let buffer = buffer.as_mut(); let cursor = ZCursor::new(to_decode.as_ref()); @@ -38,7 +44,10 @@ impl Decoder for MJpegDecoder { let mut config = self.config.decoder_options; if let Some(dest_hint) = destination_format_hint { - config = self.config.decoder_options.jpeg_set_out_colorspace(dest_hint.into()); + config = self + .config + .decoder_options + .jpeg_set_out_colorspace(dest_hint.into()); } decoder.set_options(config); @@ -47,39 +56,64 @@ impl Decoder for MJpegDecoder { let info = match decoder.info() { Some(i) => i, - None => return Err(NokhwaError::Decoder("??????? how did we get here".to_string())) + None => { + return Err(NokhwaError::Decoder( + "??????? how did we get here".to_string(), + )); + } }; Ok(info.into()) } - fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>) -> Result + fn decode_to_pixel_buffer( + &mut self, + to_decode: FrameBuffer, + mut buffer: impl AsMut<[P::Subpixel]>, + ) -> Result where -

::Subpixel: NonFloatScalarWidth +

::Subpixel: NonFloatScalarWidth, { let hint = match pixel_to_colorspace::

() { Some(cs) => cs, - None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)) + None => { + return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat( + P::COLOR_MODEL, + <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES, + )); + } }; - let meta = self.decode_to_buffer(to_decode, buffer, Some(hint))?; + let meta = self.decode_to_buffer(to_decode, cast_slice_mut(buffer.as_mut()), Some(hint))?; Ok(meta) } - fn decode(&mut self, to_decode: FrameBuffer) -> Result, NokhwaError> + fn decode( + &mut self, + to_decode: FrameBuffer, + ) -> Result, NokhwaError> where -

::Subpixel: NonFloatScalarWidth +

::Subpixel: NonFloatScalarWidth, { let min_size = self.output_decoder_min_size_pixel::

(self.config.resolution); - let mut buffer = vec![0_u8; min_size]; - let output_metadata = self.decode_to_pixel_buffer(to_decode, buffer.as_mut_slice())?; - let image_buffer = ImageBuffer::from_raw(output_metadata.resolution.width(), output_metadata.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to make imagebuffer".to_string()))?; - Ok(DecodedImage::new( - image_buffer, - output_metadata - )) + let mut out_buffer: Vec = vec![P::Subpixel::DEFAULT_MAX_VALUE; min_size]; + let output_metadata = + self.decode_to_pixel_buffer::

(to_decode, out_buffer.as_mut_slice())?; + let image_buffer = ImageBuffer::from_raw( + output_metadata.resolution.width(), + output_metadata.resolution.height(), + out_buffer, + ) + .ok_or(NokhwaError::Decoder( + "Failed to make imagebuffer".to_string(), + ))?; + Ok(DecodedImage::new(image_buffer, output_metadata)) } - fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + fn output_decoder_min_size( + &self, + resolution: Resolution, + destination_format: Self::DestinationFormatHint, + ) -> usize { let stride = match destination_format { OutputColor::Rgb => 3, OutputColor::RgbA => 4, @@ -122,7 +156,7 @@ impl From for ImageMeta { #[derive(Copy, Clone, Debug)] pub struct MJpegOptions { pub resolution: Resolution, - pub decoder_options: DecoderOptions + pub decoder_options: DecoderOptions, } #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] @@ -135,9 +169,9 @@ pub enum OutputColor { LumaA, } -impl Into for OutputColor { - fn into(self) -> ColorSpace { - match self { +impl From for ColorSpace { + fn from(val: OutputColor) -> Self { + match val { OutputColor::Rgb => ColorSpace::RGB, OutputColor::RgbA => ColorSpace::RGBA, OutputColor::Bgr => ColorSpace::BGR, @@ -148,7 +182,11 @@ impl Into for OutputColor { } } -fn pixel_to_colorspace

() -> Option where P: Pixel,

::Subpixel: NonFloatScalarWidth { +fn pixel_to_colorspace

() -> Option +where + P: Pixel, +

::Subpixel: NonFloatScalarWidth, +{ match P::COLOR_MODEL { "RGBA" => Some(OutputColor::RgbA), "RGB" => Some(OutputColor::Rgb), @@ -162,12 +200,8 @@ fn pixel_to_colorspace

() -> Option where P: Pixel,

: fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { match decode_errors { - DecodeErrors::Format(fmt) => { - NokhwaError::Decoder(fmt) - } - DecodeErrors::FormatStatic(fmt) => { - NokhwaError::Decoder(fmt.to_string()) - } + DecodeErrors::Format(fmt) => NokhwaError::Decoder(fmt), + DecodeErrors::FormatStatic(fmt) => NokhwaError::Decoder(fmt.to_string()), DecodeErrors::IllegalMagicBytes(b) => { NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}")) } @@ -177,9 +211,10 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { DecodeErrors::ZeroError => { NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string()) } - DecodeErrors::DqtError(e) | DecodeErrors::MCUError(e) | DecodeErrors::SosError(e) | DecodeErrors::SofError(e)=> { - NokhwaError::Decoder(format!("error decoding: {e}")) - } + DecodeErrors::DqtError(e) + | DecodeErrors::MCUError(e) + | DecodeErrors::SosError(e) + | DecodeErrors::SofError(e) => NokhwaError::Decoder(format!("error decoding: {e}")), DecodeErrors::Unsupported(why) => { NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}")) } @@ -192,8 +227,6 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { DecodeErrors::TooSmallOutput(a, b) => { NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}")) } - DecodeErrors::IoErrors(io) => { - NokhwaError::Decoder(format!("io error: {io:?}")) - } + DecodeErrors::IoErrors(io) => NokhwaError::Decoder(format!("io error: {io:?}")), } } diff --git a/nokhwa-decoders/src/yuv.rs b/nokhwa-decoders/src/yuv.rs index 2a3986d..259ef76 100644 --- a/nokhwa-decoders/src/yuv.rs +++ b/nokhwa-decoders/src/yuv.rs @@ -1,15 +1,35 @@ use bytemuck::{cast_slice, cast_slice_mut, try_cast_slice_mut}; + +use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive}; use nokhwa_core::error::NokhwaError; use nokhwa_core::frame_buffer::FrameBuffer; use nokhwa_core::frame_format::FrameFormat; -use nokhwa_core::types::{CameraFormat, Resolution}; -use yuv::{ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb, p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr, uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16, vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba, vyuy422_to_rgba_p16, yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba, yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr, yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra, yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb, yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra, yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange, YuvStandardMatrix}; -use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive}; use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth}; +use nokhwa_core::types::{CameraFormat, Resolution}; +use yuv::{ + YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange, + YuvStandardMatrix, ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb, + p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr, + uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16, + vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba, + vyuy422_to_rgba_p16, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba, + yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr, + yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra, + yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb, + yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba, + yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra, + yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, + yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, +}; pub struct YUVDecoder { config: YUVConfig, - stride_cache: Option, +} + +impl YUVDecoder { + pub fn new(config: ::Config) -> Self { + Self { config } + } } impl Decoder for YUVDecoder { @@ -23,85 +43,88 @@ impl Decoder for YUVDecoder { fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> { if !FrameFormat::YCBCR.contains(&config.yuv_type) { - return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type)) + return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type)); } self.config = config; - self.stride_cache = None; Ok(()) } - fn decode_to_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, destination_format: Option) -> Result { + fn decode_to_buffer( + &mut self, + to_decode: FrameBuffer<'_>, + mut buffer: impl AsMut<[u8]>, + destination_format: Option, + ) -> Result { let destination_format = match destination_format { Some(df) => df, - None => return Err(NokhwaError::DecoderDestinationHintRequired) + None => return Err(NokhwaError::DecoderDestinationHintRequired), }; - + let buffer = buffer.as_mut(); if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format) { - return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string())) + return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string())); } - let stride = match self.stride_cache { - Some(c) => c, - None => { - self.stride_cache = figure_out_stride(self.config.yuv_type); - match self.stride_cache { - Some(s) => s, - None => return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)), - } - } - }; + let stride = figure_out_stride(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?; + let byte_width = figure_out_byte_width(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?; + + + let stride_3px = 3 * + self.config.resolution.width(); + let stride_4px = 4 * self.config.resolution.width(); + let stride_3px_2w = 6 * self.config.resolution.width(); + let stride_4px_2w = 8 * self.config.resolution.width(); // todo: clean up ts into a macro { - let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride); + let decode_status = match stride { + Stride::Packed(stride) => { + let image = prepare_to_packed_image(&to_decode, self.config.resolution, byte_width, stride); match self.config.yuv_type { FrameFormat::Ayuv_32 => { match destination_format { - YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.premultiply_alpha)), - YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), _ => None, } } FrameFormat::Yuyv_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, 8, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, stride_4px_2w, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Uyvy_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Vyuy_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Yvyu_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } _ => { @@ -113,78 +136,79 @@ impl Decoder for YUVDecoder { } } } - CachedStride::SemiPlanar(y_stride, uv_stride) => { - let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, y_stride, uv_stride); + Stride::Semi(y_stride, uv_stride) => { + let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, uv_stride); match self.config.yuv_type { FrameFormat::NV24 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV42 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV16 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV61 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV12 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV21 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::P010 => { + let a = convert_bi_planar_image_to_u16(image); match destination_format { - YUVDestination::Rgb8 => Some(p010_to_rgb(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(p010_to_rgba(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(p010_to_bgr(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(p010_to_bgra(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgb16 => Some(p010_to_rgb10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(p010_to_rgba10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb16 => Some(p010_to_rgb10(&a, cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(p010_to_rgba10(&a, cast_slice_mut(buffer), stride_4px, self.config.range, self.config.matrix)), // _ => None, } } FrameFormat::P012 => { match destination_format { - YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_4px_2w, self.config.range, self.config.matrix)), _ => None, } } @@ -197,15 +221,15 @@ impl Decoder for YUVDecoder { } } } - CachedStride::Planar(y_stride, u_stride, v_stride) => { - let image = prepare_to_planar_image(&to_decode, self.config.resolution, y_stride, u_stride, v_stride); + Stride::Planar(y_stride, u_stride, v_stride, line_ratio) => { + let image = prepare_to_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, u_stride, v_stride, line_ratio); match self.config.yuv_type { FrameFormat::Yuv_4_2_0 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), _ => None, } } @@ -222,46 +246,68 @@ impl Decoder for YUVDecoder { match decode_status { Some(Ok(_)) => Ok(()), Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())), - None => Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)), + None => Err(NokhwaError::DecoderUnsupportedFrameFormat( + self.config.yuv_type, + )), } } - fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[P::Subpixel]>) -> Result + fn decode_to_pixel_buffer( + &mut self, + to_decode: FrameBuffer<'_>, + mut buffer: impl AsMut<[P::Subpixel]>, + ) -> Result where -

::Subpixel: NonFloatScalarWidth +

::Subpixel: NonFloatScalarWidth, { let destination = match YUVDestination::get_by_pixel::

() { - None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)), + None => { + return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat( + P::COLOR_MODEL, + <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES, + )); + } Some(d) => d, }; let buffer = buffer.as_mut(); let cast_slice = try_cast_slice_mut::(buffer) .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?; - + self.decode_to_buffer(to_decode, cast_slice, Some(destination)) } - fn decode(&mut self, to_decode: FrameBuffer<'_>) -> Result, NokhwaError> + fn decode( + &mut self, + to_decode: FrameBuffer<'_>, + ) -> Result, NokhwaError> where -

::Subpixel: NonFloatScalarWidth +

::Subpixel: NonFloatScalarWidth, { let min_size_alloc = self.output_decoder_min_size_pixel::

(self.config.resolution); let mut out_buffer: Vec = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc]; self.decode_to_pixel_buffer::

(to_decode, &mut out_buffer)?; - Ok( - DecodedImage::new( - ImageBuffer::from_vec(self.config.resolution.width(), self.config.resolution.height(), out_buffer) - .ok_or(NokhwaError::Decoder("failed to convert into an image buffer".to_string()))?, - () + Ok(DecodedImage::new( + ImageBuffer::from_vec( + self.config.resolution.width(), + self.config.resolution.height(), + out_buffer, ) - ) + .ok_or(NokhwaError::Decoder( + "failed to convert into an image buffer".to_string(), + ))?, + (), + )) } - fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + fn output_decoder_min_size( + &self, + resolution: Resolution, + destination_format: Self::DestinationFormatHint, + ) -> usize { let px_size = match destination_format { YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3, - YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4, + YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4, YUVDestination::Rgb16 => 3 * 2, YUVDestination::Rgba16 => 4 * 2, }; @@ -285,30 +331,32 @@ pub enum YUVDestination { } impl YUVDestination { - pub fn get_by_pixel

() -> Option where P: Pixel,

::Subpixel: NonFloatScalarWidth { + pub fn get_by_pixel

() -> Option + where + P: Pixel, +

::Subpixel: NonFloatScalarWidth, + { match P::COLOR_MODEL { - "RGB" => { - match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { - 1 => Some(YUVDestination::Rgb8), - 2 => Some(YUVDestination::Rgb16), - _ => None, - } - } - "RGBA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + "RGB" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + 1 => Some(YUVDestination::Rgb8), + 2 => Some(YUVDestination::Rgb16), + _ => None, + }, + "RGBA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { 1 => Some(YUVDestination::Rgba8), 2 => Some(YUVDestination::Rgba16), _ => None, - } - "BGR" =>match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + }, + "BGR" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { 1 => Some(YUVDestination::Bgr8), // 2 => Some(YUVDestination::Bgr16), _ => None, - } + }, "BGRA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { 1 => Some(YUVDestination::Bgra8), // 2 => Some(YUVDestination::Bgra16), _ => None, - } + }, _ => None, } } @@ -329,7 +377,7 @@ impl TryFrom for YUVConfig { fn try_from(value: CameraFormat) -> Result { if !FrameFormat::YCBCR.contains(&value.format()) { - return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format())) + return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format())); } Ok(YUVConfig { resolution: value.resolution(), @@ -342,33 +390,69 @@ impl TryFrom for YUVConfig { } } -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] -enum CachedStride { +#[derive(Copy, Clone, Debug)] +enum Stride { Packed(u32), - SemiPlanar(u32, u32), - Planar(u32, u32, u32), + Semi(u32, u32), + Planar(u32, u32, u32, u32), } -fn figure_out_stride(frame_format: FrameFormat) -> Option { - if let Some(s) = packed_stride_component_per_4px(frame_format) { - return Some(CachedStride::Packed(s)); +fn figure_out_stride(frame_format: FrameFormat) -> Option { + if let Some(yuy) = packed_stride_component(frame_format) { + return Some(Stride::Packed(yuy)); } - if let Some((s1, s2)) = semiplanar_stride_per_4px(frame_format) { - return Some(CachedStride::SemiPlanar(s1, s2)); + if let Some((y, uv)) = semiplanar_stride(frame_format) { + return Some(Stride::Semi(y, uv)); } - if let Some((s1, s2, s3)) = planar_stride_per_4px(frame_format) { - return Some(CachedStride::Planar(s1, s2, s3)); + // l here means the ratio of luma lines to chroma lines + // u_r and v_r are defined as _ratios_ to the luma stride, i.e. how many luma components + // per chroma component, u_r = 2 means 2 luma per 1 u chroma + if let Some((y, u_r, v_r, l)) = planar_stride(frame_format) { + return Some(Stride::Planar(y, u_r, v_r, l)); } None } -fn packed_stride_component_per_4px(format: FrameFormat) -> Option { +fn figure_out_byte_width(format: FrameFormat) -> Option { match format { - FrameFormat::Ayuv_32 => Some(64), + FrameFormat::Ayuv_32 | FrameFormat::Yuyv_4_2_2 + | FrameFormat::Uyvy_4_2_2 + | FrameFormat::Vyuy_4_2_2 + | FrameFormat::Yvyu_4_2_2 => Some(1), + FrameFormat::NV24 | FrameFormat::NV42 | FrameFormat::NV16 | FrameFormat::NV61 | FrameFormat::NV12 | FrameFormat::NV21 => Some(1), + FrameFormat::P010 | FrameFormat::P012 => Some(2), + FrameFormat::Yuv_4_2_0 => Some(1), + _ => None, + } +} + +fn packed_stride_component(format: FrameFormat) -> Option { + match format { + FrameFormat::Ayuv_32 => Some(4), FrameFormat::Yuyv_4_2_2 | FrameFormat::Uyvy_4_2_2 | FrameFormat::Vyuy_4_2_2 - | FrameFormat::Yvyu_4_2_2 => Some(16), + | FrameFormat::Yvyu_4_2_2 => Some(2), + _ => None, + } +} + +fn semiplanar_stride(format: FrameFormat) -> Option<(u32, u32)> { + match format { + FrameFormat::NV24 | FrameFormat::NV42 => Some((1, 2)), + FrameFormat::NV16 | FrameFormat::NV61 => Some((1, 1)), + FrameFormat::P010 | FrameFormat::P012 => Some((1, 1)), + FrameFormat::NV12 | FrameFormat::NV21 => Some((1, 1)), + _ => None, + } +} + +fn planar_stride(format: FrameFormat) -> Option<(u32, u32, u32, u32)> { + match format { + // if you are here wondering if I will ever add another planar format + // the answer is no. + // do not bother opening an issue or a pr i will never merge it use a sane format like nv12 + FrameFormat::Yuv_4_2_0 => Some((1, 2, 2, 2)), _ => None, } } @@ -376,77 +460,79 @@ fn packed_stride_component_per_4px(format: FrameFormat) -> Option { fn prepare_to_packed_image<'a>( frame_buffer: &'a FrameBuffer<'a>, resolution: Resolution, + byte_width: u32, yuy_stride: u32, ) -> YuvPackedImage<'a, u8> { YuvPackedImage { yuy: frame_buffer.buffer(), - yuy_stride, + yuy_stride: yuy_stride * resolution.width(), width: resolution.width(), height: resolution.height(), } } -fn semiplanar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32)> { - match format { - FrameFormat::NV24 | FrameFormat::NV42 => Some((4, 8)), - FrameFormat::NV16 | FrameFormat::NV61 => Some((4, 4)), - FrameFormat::NV12 | FrameFormat::NV21 | FrameFormat::P010 | FrameFormat::P012 => { - Some((4, 4)) - } - _ => None, - } -} - fn prepare_to_semi_planar_image<'a>( frame_buffer: &'a FrameBuffer<'a>, resolution: Resolution, + byte_width: u32, y_stride: u32, uv_stride: u32, ) -> YuvBiPlanarImage<'a, u8> { - let uv_start = (resolution.width() * resolution.height()) as usize; + let uv_start = (resolution.width() * resolution.height() * byte_width) as usize; YuvBiPlanarImage { y_plane: &frame_buffer.buffer()[0..uv_start], - y_stride, + y_stride: y_stride * resolution.width(), uv_plane: &frame_buffer.buffer()[uv_start..frame_buffer.len()], - uv_stride, + uv_stride: uv_stride * resolution.width(), width: resolution.width(), height: resolution.height(), } } -fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> { - match format { - FrameFormat::Yuv_4_2_0 => Some((4, 1, 1)), - _ => None, - } -} +// fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> { +// match format { +// FrameFormat::Yuv_4_2_0 => Some((stride_ 1, 1)), +// _ => None, +// } +// } +// does this work? idk fn prepare_to_planar_image<'a>( frame_buffer: &'a FrameBuffer<'a>, resolution: Resolution, - y_stride: u32, - u_stride: u32, - v_stride: u32, + byte_width: u32, + y_stride_base: u32, + u_stride_ratio: u32, + v_stride_ratio: u32, + line_ratio: u32, ) -> YuvPlanarImage<'a, u8> { - let u_start = (resolution.width() * resolution.height()) as usize; - let v_start = - u_start + ((resolution.width() / 4) * y_stride * (resolution.height() * u_stride)) as usize; + + let y_stride = resolution.width() * y_stride_base; + let u_stride = y_stride / u_stride_ratio; + let v_stride = y_stride / v_stride_ratio; + let chroma_lines = resolution.height() / line_ratio; + + // size of y area + let u_start = resolution.height() * resolution.width() * byte_width; + let v_start = u_start + u_stride * chroma_lines * byte_width; + + let us = u_start as usize; + let vs = v_start as usize; + YuvPlanarImage { - y_plane: &frame_buffer.buffer()[0..u_start], + y_plane: &frame_buffer.buffer()[0..us], y_stride, - u_plane: &frame_buffer.buffer()[u_start..v_start], + u_plane: &frame_buffer.buffer()[us..vs], u_stride, - v_plane: &frame_buffer.buffer()[v_start..frame_buffer.len()], + v_plane: &frame_buffer.buffer()[vs..frame_buffer.len()], v_stride, width: resolution.width(), height: resolution.height(), } } -fn convert_packed_image_to_u16( - yuv_packed_image: YuvPackedImage, -) -> YuvPackedImage { - let buf= cast_slice(yuv_packed_image.yuy); +fn convert_packed_image_to_u16(yuv_packed_image: YuvPackedImage) -> YuvPackedImage { + let buf = cast_slice(yuv_packed_image.yuy); YuvPackedImage { yuy: buf, yuy_stride: yuv_packed_image.yuy_stride, @@ -458,8 +544,8 @@ fn convert_packed_image_to_u16( fn convert_bi_planar_image_to_u16( yuv_bi_planar_image: YuvBiPlanarImage, ) -> YuvBiPlanarImage { - let buf_y= cast_slice(yuv_bi_planar_image.y_plane); - let buf_uv= cast_slice(yuv_bi_planar_image.uv_plane); + let buf_y = cast_slice(yuv_bi_planar_image.y_plane); + let buf_uv = cast_slice(yuv_bi_planar_image.uv_plane); YuvBiPlanarImage { y_plane: buf_y, y_stride: yuv_bi_planar_image.y_stride, @@ -469,3 +555,224 @@ fn convert_bi_planar_image_to_u16( height: yuv_bi_planar_image.height, } } + +#[cfg(test)] +mod test { + use crate::yuv::{YUVConfig, YUVDecoder}; + use image::{DynamicImage, EncodableLayout, ImageBuffer, ImageFormat, ImageReader, Pixel, PixelWithColorType, Rgb, Rgba}; + use nokhwa_core::decoder::Decoder; + use nokhwa_core::frame_buffer::FrameBuffer; + use nokhwa_core::frame_format::FrameFormat; + use nokhwa_core::image::NonFloatScalarWidth; + use nokhwa_core::types::Resolution; + use std::borrow::Cow; + use std::fs::File; + use std::io::{BufReader, BufWriter, Read}; + use yuv::{YuvConversionMode, YuvRange, YuvStandardMatrix}; + + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + enum PixelFormat { + Rgb8, + RgbA8, + Rgb16, + RgbA16, + } + + fn base_test(filename: String, resolution: Resolution, format: FrameFormat) -> ImageBuffer> +where +

::Subpixel: NonFloatScalarWidth { + let mut file = File::open(filename).unwrap(); + let mut nv12_data = Vec::new(); + file.read_to_end(&mut nv12_data).unwrap(); + + let mut decoder = YUVDecoder::new( + YUVConfig { + resolution, + yuv_type: format, + range: YuvRange::Full, + matrix: YuvStandardMatrix::Bt601, + mode: YuvConversionMode::Balanced, + premultiply_alpha: false, + } + ); + + let frame_buffer = FrameBuffer::new(Cow::Owned(nv12_data), None); + decoder.decode::

(frame_buffer).unwrap().buffer + } + + fn write_image(image: ImageBuffer>, filename: String) where + [

::Subpixel]: EncodableLayout + { + image.save_with_format(filename, ImageFormat::Png).unwrap(); + } + + fn load_image(filename: String, format: ImageFormat) -> DynamicImage { + let file = File::open(filename).unwrap(); + let image = image::load(BufReader::new(file), format).unwrap(); + image + } + + #[test] + fn test_nv12() { + let base_filename = "test_images/yuv/nv12/crimeandsekai"; + let resolution = Resolution::new(1024, 1520); + let format = FrameFormat::NV12; + let img_format = ImageFormat::Png; + + // test nv12 rgb8 + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(out.as_raw(), image_rgb8.as_raw()); + } + // test nv12 rgba8 + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + + assert_eq!(out.as_raw(), image_rgba8.as_raw()); + } + } + + // FIXME: Can anyone make a 32bit AYUV image? I can't. Reenable this test after doing so. + // #[test] + // fn test_ayuv() { + // let base_filename = "test_images/yuv/ayuv/lhzlings"; + // let resolution = Resolution::new(1000, 1080); + // let format = FrameFormat::Ayuv_32; + // let img_format = ImageFormat::Png; + // + // { + // // let image_rgb8 = load_image(format!("{base_filename}.rgb")) + // let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + // + // write_image(out, format!("{base_filename}.rgb8.png")) + // } + // + // { + // let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + // + // write_image(out, format!("{base_filename}.rgba8.png")) + // } + // } + + #[test] + fn test_nv24() { + let base_filename = "test_images/yuv/nv24/larihole"; + let resolution = Resolution::new(800, 600); + let format = FrameFormat::NV24; + let img_format = ImageFormat::Png; + + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgb8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgb8.png")); + } + + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgba8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgba8.png")); + + } + } + + #[test] + fn test_nv16() { + let base_filename = "test_images/yuv/nv16/aeq"; + let resolution = Resolution::new(802, 602); + let format = FrameFormat::NV16; + let img_format = ImageFormat::Png; + + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgb8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgb8.png")); + } + + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgba8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgba8.png")); + } + } + + #[test] + fn test_p010() { + let base_filename = "test_images/yuv/p010/crimesagainstvalor"; + let resolution = Resolution::new(128, 128); + let format = FrameFormat::P010; + let img_format = ImageFormat::Png; + + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgb8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgb8.png")); + } + + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgba8.as_raw(), out.as_raw()); + // write_image(out, format!("{base_filename}.rgba8.png")); + } + + // there is some kind of bug in image-rs or smth idk cba to figure it out + // { + // let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + // // write_image(out, format!("{base_filename}.rgb16.png")); + // out.save_with_format(format!("{base_filename}.rgb16.avif"), ImageFormat::Avif).unwrap(); + // } + } + + #[test] + fn test_yuv420() { + let base_filename = "test_images/yuv/yuv420/youarethemurderr"; + let resolution = Resolution::new(460, 460); + let format = FrameFormat::Yuv_4_2_0; + let img_format = ImageFormat::Png; + + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + // write_image(out, format!("{base_filename}.rgb8.png")); + assert_eq!(image_rgb8.as_raw(), out.as_raw()); + } + + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + // write_image(out, format!("{base_filename}.rgba8.png")); + assert_eq!(image_rgba8.as_raw(), out.as_raw()); + } + } + + #[test] + fn test_yuyv() { + let base_filename = "test_images/yuv/yuyv/20250530_224336"; + let resolution = Resolution::new(3024, 4032); + let format = FrameFormat::Yuyv_4_2_2; + let img_format = ImageFormat::Png; + + { + let image_rgb8 = load_image(format!("{base_filename}.rgb8.png"), img_format).to_rgb8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgb8.as_raw(), out.as_raw()); + } + + { + let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8(); + let out = base_test::>(format!("{base_filename}.yuv"), resolution, format); + assert_eq!(image_rgba8.as_raw(), out.as_raw()); + } + } + + +} diff --git a/nokhwa-decoders/test_images/notes.txt b/nokhwa-decoders/test_images/notes.txt new file mode 100644 index 0000000..8d0f932 --- /dev/null +++ b/nokhwa-decoders/test_images/notes.txt @@ -0,0 +1,7 @@ + +ffmpeg -i crimeandsekai.png -pix_fmt nv12 -f rawvideo crimeandsekai.yuv + +ffmpeg -s 1024x1520 -pix_fmt nv12 -i crimeandsekai.yuv -f image2 -pix_fmt rgb24 crimeandsekai.nv12.png + + +AYUV will fail on BE machines :D \ No newline at end of file diff --git a/src/camera.rs b/src/camera.rs index 170079a..e69de29 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,158 +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. - */ - -use nokhwa_core::format_request::FormatFilter; -use nokhwa_core::frame_format::SourceFrameFormat; -use nokhwa_core::traits::Backend; -use nokhwa_core::{ - frame_buffer::FrameBuffer, - error::NokhwaError, - pixel_format::FormatDecoder, - traits::CaptureTrait, - types::{ - ApiBackend, CameraFormat, CameraIndex, CameraInformation - , RequestedFormatType, Resolution, - }, -}; -use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; - -/// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use. -pub struct Camera { - idx: CameraIndex, - api: ApiBackend, - device: Box, -} - -impl Camera { - pub fn new() -> Result {} - - pub fn with_api_backend() -> Result {} - - pub fn with_custom_backend() -> Result {} -} - -impl CaptureTrait for Camera { - fn init(&mut self) -> Result<(), NokhwaError> { - todo!() - } - - fn init_with_format(&mut self, format: FormatFilter) -> Result { - todo!() - } - - fn backend(&self) -> ApiBackend { - todo!() - } - - fn camera_info(&self) -> &CameraInformation { - todo!() - } - - fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> { - todo!() - } - - fn camera_format(&self) -> Option { - todo!() - } - - fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> { - todo!() - } - - fn compatible_list_by_resolution( - &mut self, - fourcc: SourceFrameFormat, - ) -> Result>, NokhwaError> { - todo!() - } - - fn compatible_fourcc(&mut self) -> Result, NokhwaError> { - todo!() - } - - fn resolution(&self) -> Option { - todo!() - } - - fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> { - todo!() - } - - fn frame_rate(&self) -> Option { - todo!() - } - - fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> { - todo!() - } - - fn frame_format(&self) -> SourceFrameFormat { - todo!() - } - - fn set_frame_format( - &mut self, - fourcc: impl Into, - ) -> Result<(), NokhwaError> { - todo!() - } - - fn camera_control(&self, control: KnownCameraControl) -> Result { - todo!() - } - - fn camera_controls(&self) -> Result, NokhwaError> { - todo!() - } - - fn set_camera_control( - &mut self, - id: KnownCameraControl, - value: ControlValue, - ) -> Result<(), NokhwaError> { - todo!() - } - - fn open_stream(&mut self) -> Result<(), NokhwaError> { - todo!() - } - - fn is_stream_open(&self) -> bool { - todo!() - } - - fn frame(&mut self) -> Result { - todo!() - } - - fn frame_raw(&mut self) -> Result, NokhwaError> { - todo!() - } - - fn stop_stream(&mut self) -> Result<(), NokhwaError> { - todo!() - } -} - -impl Drop for Camera { - fn drop(&mut self) { - self.stop_stream().unwrap(); - } -} - -unsafe impl Send for Camera {}