mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
add test images, add tests for yuv decoder
This commit is contained in:
+52
-40
@@ -13,16 +13,16 @@ repository = "https://github.com/l1npengtul/nokhwa"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "nokhwa-decoders"]
|
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "nokhwa-decoders"]
|
||||||
exclude = ["examples/*"]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["decoding-yuv","decoding-mozjpeg"]
|
default = ["decoding-yuv","decoding-mjpeg"]
|
||||||
serialize = ["serde", "nokhwa-core/serialize"]
|
serialize = ["serde", "nokhwa-core/serialize"]
|
||||||
decoding-yuv = ["mozjpeg"]
|
decoding-yuv = ["nokhwa-decoders/yuv"]
|
||||||
decoding-mozjpeg = ["mozjpeg"]
|
decoding-mjpeg = ["nokhwa-decoders/mjpeg"]
|
||||||
|
decoding-ffmpeg = ["nokhwa-decoders/ffmpeg"]
|
||||||
input-avfoundation = ["nokhwa-bindings-macos", "flume"]
|
input-avfoundation = ["nokhwa-bindings-macos", "flume"]
|
||||||
input-msmf = ["nokhwa-bindings-windows"]
|
input-msmf = ["nokhwa-bindings-windows"]
|
||||||
input-v4l = ["nokhwa-bindings-linux"]
|
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-uvc = ["uvc", "uvc/vendor", "usb_enumeration", "lazy_static"]
|
||||||
input-opencv = ["opencv", "opencv/rgb", "rgb", "nokhwa-core/opencv-mat"]
|
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"]
|
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-wasm = ["input-jscam"]
|
||||||
output-threaded = []
|
output-threaded = []
|
||||||
output-async = ["nokhwa-core/async", "async-trait"]
|
output-async = ["nokhwa-core/async", "async-trait"]
|
||||||
@@ -43,44 +43,10 @@ test-fail-warning = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
|
|
||||||
[dependencies.mozjpeg]
|
|
||||||
version = "0.10"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.nokhwa-core]
|
[dependencies.nokhwa-core]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
path = "nokhwa-core"
|
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]
|
[dependencies.nokhwa-bindings-windows]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
path = "nokhwa-bindings-windows"
|
path = "nokhwa-bindings-windows"
|
||||||
@@ -96,6 +62,37 @@ version = "0.2"
|
|||||||
path = "nokhwa-bindings-linux"
|
path = "nokhwa-bindings-linux"
|
||||||
optional = true
|
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]
|
[dependencies.web-sys]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
features = [
|
features = [
|
||||||
@@ -144,3 +141,18 @@ optional = true
|
|||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["docs-only", "docs-nolink", "docs-features"]
|
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
|
||||||
|
|||||||
Generated
+6
-6
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751498133,
|
"lastModified": 1755020227,
|
||||||
"narHash": "sha256-QWJ+NQbMU+NcU2xiyo7SNox1fAuwksGlQhpzBl76g1I=",
|
"narHash": "sha256-gGmm+h0t6rY88RPTaIm3su95QvQIVjAJx558YUG4Id8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d55716bb59b91ae9d1ced4b1ccdea7a442ecbfdb",
|
"rev": "695d5db1b8b20b73292501683a524e0bd79074fb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -62,11 +62,11 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751510438,
|
"lastModified": 1755052812,
|
||||||
"narHash": "sha256-m8PjOoyyCR4nhqtHEBP1tB/jF+gJYYguSZmUmVTEAQE=",
|
"narHash": "sha256-Tjw2YP7Hz8+ibE8wJ+Ps65vh1lzAe5ozmoo9sdQ7rGg=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "7f415261f298656f8164bd636c0dc05af4e95b6b",
|
"rev": "433023cba5f4fa66b8b0fdbb8f91d420c9cc2527",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -5,57 +5,97 @@
|
|||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs =
|
||||||
self,
|
{
|
||||||
nixpkgs,
|
self,
|
||||||
rust-overlay,
|
nixpkgs,
|
||||||
flake-utils,
|
rust-overlay,
|
||||||
...
|
flake-utils,
|
||||||
}:
|
...
|
||||||
|
}:
|
||||||
flake-utils.lib.eachDefaultSystem (
|
flake-utils.lib.eachDefaultSystem (
|
||||||
system: let
|
system:
|
||||||
|
let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system;
|
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 {
|
toolchain.default.override {
|
||||||
extensions = ["rust-src" "clippy" "rustfmt" "miri"];
|
extensions = [
|
||||||
});
|
"rust-src"
|
||||||
in {
|
"clippy"
|
||||||
|
"rustfmt"
|
||||||
|
"miri"
|
||||||
|
"rust-analyzer"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
formatter = pkgs.alejandra;
|
formatter = pkgs.alejandra;
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = rustshell {
|
||||||
packages = [
|
packages = [
|
||||||
rustbin
|
rustbin
|
||||||
] ++ (with pkgs; [
|
]
|
||||||
llvmPackages.libclang.lib
|
++ (with pkgs; [
|
||||||
llvmPackages.clang
|
llvmPackages_21.clangWithLibcAndBasicRtAndLibcxx
|
||||||
pkg-config
|
pkg-config
|
||||||
cmake
|
cmake
|
||||||
vcpkg
|
vcpkg
|
||||||
rustPlatform.bindgenHook
|
lldb
|
||||||
xmlstarlet
|
rustPlatform.bindgenHook
|
||||||
opencv
|
xmlstarlet
|
||||||
alsa-lib
|
opencv
|
||||||
systemdLibs
|
alsa-lib
|
||||||
cmake
|
systemdLibs
|
||||||
fontconfig
|
cmake
|
||||||
linuxHeaders
|
fontconfig
|
||||||
v4l-utils
|
linuxHeaders
|
||||||
libv4l
|
v4l-utils
|
||||||
pipewire
|
libv4l
|
||||||
rustup
|
pipewire
|
||||||
ffmpeg-full
|
rustup
|
||||||
nasm
|
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.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library";
|
||||||
env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
|
|
||||||
shellHook = let
|
shellHook =
|
||||||
pathToRustProject = "/project/component[@name='RustProjectSettings']";
|
let
|
||||||
in
|
pathToRustProject = "/project/component[@name='RustProjectSettings']";
|
||||||
|
in
|
||||||
''
|
''
|
||||||
echo "WONDERHOOOOOY!!!!"
|
echo "WONDERHOOOOOY!!!!"
|
||||||
xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='explicitPathToStdlib']/@value" --value "${rustbin}/lib/rustlib/src/rust/library" .idea/workspace.xml
|
xmlstarlet edit --inplace --update "${pathToRustProject}/option[@name='explicitPathToStdlib']/@value" --value "${rustbin}/lib/rustlib/src/rust/library" .idea/workspace.xml
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ typed-builder = "0.21"
|
|||||||
compact_str = "0.9"
|
compact_str = "0.9"
|
||||||
bytemuck = "1.23"
|
bytemuck = "1.23"
|
||||||
smallmap = "1.4"
|
smallmap = "1.4"
|
||||||
constcat = "0.6"
|
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
|
|
||||||
[dependencies.num-rational]
|
[dependencies.num-rational]
|
||||||
@@ -38,21 +37,18 @@ default-features = false
|
|||||||
features = ["serde", "std"]
|
features = ["serde", "std"]
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
version = "0.25"
|
workspace = true
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
workspace = true
|
||||||
features = ["derive"]
|
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.wgpu-types]
|
[dependencies.wgpu-types]
|
||||||
version = "25"
|
workspace = true
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.opencv]
|
[dependencies.opencv]
|
||||||
version = "0.94"
|
workspace = true
|
||||||
default-features = false
|
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.async-trait]
|
[dependencies.async-trait]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::error::NokhwaError;
|
use crate::error::NokhwaError;
|
||||||
use crate::frame_buffer::FrameBuffer;
|
use crate::frame_buffer::FrameBuffer;
|
||||||
use crate::image::{DecodedImage, NonFloatScalarWidth};
|
use crate::image::{DecodedImage, NonFloatScalarWidth};
|
||||||
use crate::types::{CameraFormat, Resolution};
|
use crate::types::{Resolution};
|
||||||
pub use image::{ImageBuffer, Pixel, Primitive};
|
pub use image::{ImageBuffer, Pixel, Primitive};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
use crate::frame_format::FrameFormat;
|
use crate::frame_format::FrameFormat;
|
||||||
use crate::platform::Backends;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use crate::types::Backends;
|
||||||
|
|
||||||
pub type NokhwaResult<T> = Result<T, NokhwaError>;
|
pub type NokhwaResult<T> = Result<T, NokhwaError>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use bytemuck::Pod;
|
use bytemuck::Pod;
|
||||||
use image::{ImageBuffer, Pixel, Primitive};
|
pub use image::{ImageBuffer, Pixel, Primitive};
|
||||||
use num_traits::{NumCast, PrimInt};
|
use num_traits::{NumCast, PrimInt};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|||||||
@@ -1,23 +1,8 @@
|
|||||||
use crate::camera::Camera;
|
use crate::camera::Camera;
|
||||||
use crate::error::NokhwaResult;
|
use crate::error::NokhwaResult;
|
||||||
use crate::types::{CameraIndex, CameraInformation};
|
use crate::types::{Backends, CameraIndex, CameraInformation, QueriedCamera};
|
||||||
use std::fmt::{Display, Formatter};
|
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 {
|
pub trait PlatformTrait {
|
||||||
const PLATFORM: Backends;
|
const PLATFORM: Backends;
|
||||||
@@ -27,7 +12,7 @@ pub trait PlatformTrait {
|
|||||||
|
|
||||||
fn check_permission_given(&mut self) -> bool;
|
fn check_permission_given(&mut self) -> bool;
|
||||||
|
|
||||||
fn query(&mut self) -> NokhwaResult<Vec<CameraInformation>>;
|
fn query(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
|
||||||
|
|
||||||
fn open(&mut self, index: CameraIndex) -> NokhwaResult<Self::Camera>;
|
fn open(&mut self, index: CameraIndex) -> NokhwaResult<Self::Camera>;
|
||||||
|
|
||||||
@@ -47,7 +32,7 @@ pub trait AsyncPlatformTrait: PlatformTrait {
|
|||||||
|
|
||||||
async fn await_permission(&mut self) -> NokhwaResult<()>;
|
async fn await_permission(&mut self) -> NokhwaResult<()>;
|
||||||
|
|
||||||
async fn query_async(&mut self) -> NokhwaResult<Vec<CameraInformation>>;
|
async fn query_async(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
|
||||||
|
|
||||||
async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
|
async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
|
||||||
|
|
||||||
|
|||||||
+51
-141
@@ -44,9 +44,8 @@ impl CameraIndex {
|
|||||||
pub fn as_string(&self) -> String {
|
pub fn as_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
CameraIndex::Index(i) => i.to_string(),
|
CameraIndex::Index(i) => i.to_string(),
|
||||||
CameraIndex::String(s) => s.to_string(),
|
CameraIndex::String(s) | CameraIndex::Stable(s) => s.to_string(),
|
||||||
CameraIndex::Stable(s) => s.to_string(),
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this [`CameraIndex`] contains an [`CameraIndex::Index`]
|
/// Returns true if this [`CameraIndex`] contains an [`CameraIndex::Index`]
|
||||||
@@ -133,6 +132,7 @@ impl Resolution {
|
|||||||
/// Get the x (width) of Resolution
|
/// Get the x (width) of Resolution
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[deprecated]
|
||||||
pub fn x(self) -> u32 {
|
pub fn x(self) -> u32 {
|
||||||
self.width
|
self.width
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,7 @@ impl Resolution {
|
|||||||
/// Get the y (height) of Resolution
|
/// Get the y (height) of Resolution
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[deprecated]
|
||||||
pub fn y(self) -> u32 {
|
pub fn y(self) -> u32 {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ impl Resolution {
|
|||||||
|
|
||||||
impl Display for Resolution {
|
impl Display for Resolution {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
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 {
|
impl Ord for Resolution {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match self.x().cmp(&other.x()) {
|
match self.width().cmp(&other.width()) {
|
||||||
Ordering::Less => Ordering::Less,
|
Ordering::Less => Ordering::Less,
|
||||||
Ordering::Equal => self.y().cmp(&other.y()),
|
Ordering::Equal => self.height().cmp(&other.height()),
|
||||||
Ordering::Greater => Ordering::Greater,
|
Ordering::Greater => Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,11 +175,11 @@ impl Ord for Resolution {
|
|||||||
|
|
||||||
impl Distance<u32> for Resolution {
|
impl Distance<u32> for Resolution {
|
||||||
fn distance_from(&self, other: &Self) -> u32 {
|
fn distance_from(&self, other: &Self) -> u32 {
|
||||||
let x1 = self.x();
|
let x1 = self.width();
|
||||||
let x2 = other.x();
|
let x2 = other.width();
|
||||||
|
|
||||||
let y1 = self.y();
|
let y1 = self.height();
|
||||||
let y2 = other.y();
|
let y2 = other.height();
|
||||||
|
|
||||||
(x2 - x1).pow(2) + (y2 - y1).pow(2)
|
(x2 - x1).pow(2) + (y2 - y1).pow(2)
|
||||||
}
|
}
|
||||||
@@ -188,8 +189,8 @@ impl Div for Resolution {
|
|||||||
type Output = Resolution;
|
type Output = Resolution;
|
||||||
|
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
let x_div = self.x().div(rhs.x());
|
let x_div = self.width().div(rhs.width());
|
||||||
let y_div = self.y().div(rhs.y());
|
let y_div = self.height().div(rhs.height());
|
||||||
Resolution::new(x_div, y_div)
|
Resolution::new(x_div, y_div)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,8 +199,8 @@ impl Sub for Resolution {
|
|||||||
type Output = Resolution;
|
type Output = Resolution;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
let x_sub = self.x().sub(rhs.x());
|
let x_sub = self.width().sub(rhs.width());
|
||||||
let y_sub = self.y().sub(rhs.y());
|
let y_sub = self.height().sub(rhs.height());
|
||||||
Resolution::new(x_sub, y_sub)
|
Resolution::new(x_sub, y_sub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,8 +209,8 @@ impl Rem for Resolution {
|
|||||||
type Output = Resolution;
|
type Output = Resolution;
|
||||||
|
|
||||||
fn rem(self, rhs: Self) -> Self::Output {
|
fn rem(self, rhs: Self) -> Self::Output {
|
||||||
let x_rem = self.x().rem(rhs.x());
|
let x_rem = self.width().rem(rhs.width());
|
||||||
let y_rem = self.y().rem(rhs.y());
|
let y_rem = self.height().rem(rhs.height());
|
||||||
Resolution::new(x_rem, y_rem)
|
Resolution::new(x_rem, y_rem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,15 +430,15 @@ impl Display for CameraFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a Camera e.g. its name.
|
/// 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.
|
/// `description` and `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.
|
/// `stable_id` contains the stable ID that may be used to reopen the same device.
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||||
pub struct CameraInformation {
|
pub struct CameraInformation {
|
||||||
human_name: String,
|
human_name: String,
|
||||||
description: String,
|
description: String,
|
||||||
misc: String,
|
misc: String,
|
||||||
index: CameraIndex,
|
stable_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CameraInformation {
|
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<str> + ?Sized)` ????
|
// OK, i just checkeed back on this code. WTF was I on when I wrote `&(impl AsRef<str> + ?Sized)` ????
|
||||||
// I need to get on the same shit that my previous self was on, because holy shit that stuff is strong as FUCK!
|
// 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.
|
// 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<String>) -> Self {
|
||||||
CameraInformation {
|
CameraInformation {
|
||||||
human_name,
|
human_name,
|
||||||
description,
|
description,
|
||||||
misc,
|
misc,
|
||||||
index,
|
stable_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,146 +460,55 @@ impl CameraInformation {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
// yes, i know, unnecessary alloc this, unnecessary alloc that
|
// yes, i know, unnecessary alloc this, unnecessary alloc that
|
||||||
// but wasm bindgen
|
// but wasm bindgen
|
||||||
pub fn human_name(&self) -> String {
|
pub fn human_name(&self) -> &str {
|
||||||
self.human_name.clone()
|
&self.human_name
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the device info's description.
|
/// Get a reference to the device info's description.
|
||||||
/// # JS-WASM
|
|
||||||
/// This is exported as a `get_Description`.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn description(&self) -> &str {
|
pub fn description(&self) -> &str {
|
||||||
self.description.borrow()
|
&self.description
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the device info's misc.
|
/// Get a reference to the device info's misc.
|
||||||
/// # JS-WASM
|
|
||||||
/// This is exported as a `get_MiscString`.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn misc(&self) -> String {
|
pub fn misc(&self) -> &str {
|
||||||
self.misc.clone()
|
&self.misc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the device info's misc.
|
#[must_use] pub fn stable_id(&self) -> Option<&str> {
|
||||||
/// # JS-WASM
|
self.stable_id.as_deref()
|
||||||
/// This is exported as a `set_MiscString`.
|
|
||||||
pub fn set_misc(&mut self, misc: &str) {
|
|
||||||
self.misc = misc.to_string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<u32, NokhwaError> {
|
|
||||||
// match &self.index {
|
|
||||||
// CameraIndex::Index(i) => Ok(*i),
|
|
||||||
// CameraIndex::String(s) => match s.parse::<u32>() {
|
|
||||||
// Ok(p) => Ok(p),
|
|
||||||
// Err(why) => Err(NokhwaError::GetPropertyError {
|
|
||||||
// property: "index-int".to_string(),
|
|
||||||
// error: why.to_string(),
|
|
||||||
// }),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CameraInformation {
|
impl Display for CameraInformation {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Name: {}, Description: {}, Extra: {}, Index: {}",
|
"Name: {}, Description: {}, Extra: {}, Stable Index: {:?}",
|
||||||
self.human_name, self.description, self.misc, self.index
|
self.human_name, self.description, self.misc, self.stable_id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn step_chk(val: i64, default: i64, step: i64) -> Result<(), NokhwaError> {
|
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
// if (val - default) % step != 0 {
|
pub enum Backends {
|
||||||
// return Err(NokhwaError::StructureError {
|
Video4Linux2,
|
||||||
// structure: "Value".to_string(),
|
WebWASM,
|
||||||
// error: "Doesnt fit step".to_string(),
|
AVFoundation,
|
||||||
// });
|
MicrosoftMediaFoundation,
|
||||||
// }
|
OpenCV,
|
||||||
// Ok(())
|
Custom(&'static str),
|
||||||
// }
|
}
|
||||||
|
|
||||||
// /// The list of known capture backends to the library. <br>
|
impl Display for Backends {
|
||||||
// /// - `Auto` - Use automatic selection.
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
// /// - `AVFoundation` - Uses `AVFoundation` on `MacOSX`
|
write!(f, "{self:?}")
|
||||||
// /// - `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.
|
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||||
// /// - `GStreamer` - ***DEPRECATED*** Uses `GStreamer` RTP to capture. Platform agnostic.
|
pub struct QueriedCamera {
|
||||||
// /// - `Browser` - Uses browser APIs to capture from a webcam.
|
pub index: CameraIndex,
|
||||||
// pub enum SelectableBackend {
|
pub information: CameraInformation
|
||||||
// Auto,
|
}
|
||||||
// Custom(&'static str),
|
|
||||||
// AVFoundation,
|
|
||||||
// Video4Linux,
|
|
||||||
// UniversalVideoClass,
|
|
||||||
// MediaFoundation,
|
|
||||||
// OpenCv,
|
|
||||||
// GStreamer,
|
|
||||||
// Browser,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// The list of known capture backends to the library. <br>
|
|
||||||
// /// - `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:?}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ edition = "2024"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
ffmpeg = ["ffmpeg-the-third"]
|
ffmpeg = ["ffmpeg-the-third"]
|
||||||
av1 = ["rav1e"]
|
|
||||||
yuyv = ["dcv-color-primitives", "yuv"]
|
yuyv = ["dcv-color-primitives", "yuv"]
|
||||||
mjpeg = ["zune-jpeg", "zune-core"]
|
mjpeg = ["zune-jpeg", "zune-core"]
|
||||||
static = ["ffmpeg-the-third/static"]
|
#static = ["ffmpeg-the-third/static"]
|
||||||
async = []
|
async = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -22,10 +21,6 @@ path = "../nokhwa-core"
|
|||||||
version = "3.0"
|
version = "3.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.rav1e]
|
|
||||||
version = "0.8"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.yuv]
|
[dependencies.yuv]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -41,3 +36,7 @@ optional = true
|
|||||||
[dependencies.zune-core]
|
[dependencies.zune-core]
|
||||||
version = "0.5.0-rc2"
|
version = "0.5.0-rc2"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dev-dependencies.image]
|
||||||
|
workspace = true
|
||||||
|
features = ["png", "jpeg", "avif"]
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ use ffmpeg_the_third::codec::{Context, Id, Parameters};
|
|||||||
use ffmpeg_the_third::decoder::Video;
|
use ffmpeg_the_third::decoder::Video;
|
||||||
use ffmpeg_the_third::ffi::{
|
use ffmpeg_the_third::ffi::{
|
||||||
AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace,
|
AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace,
|
||||||
AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket,
|
AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, AVPixelFormat, AVRational,
|
||||||
AVPixelFormat, AVRational, SwsContext, av_frame_alloc, av_frame_move_ref,
|
SwsContext, av_frame_alloc, av_frame_move_ref, av_image_copy_to_buffer, av_image_fill_arrays,
|
||||||
av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context,
|
av_image_get_buffer_size, avcodec_free_context, avcodec_parameters_alloc,
|
||||||
avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext,
|
avcodec_parameters_free, sws_freeContext, sws_getContext, sws_scale_frame,
|
||||||
sws_scale_frame,
|
|
||||||
};
|
};
|
||||||
use ffmpeg_the_third::packet::{Borrow, Ref};
|
use ffmpeg_the_third::packet::{Borrow, Ref};
|
||||||
use ffmpeg_the_third::{Frame, decoder, packet::Packet};
|
use ffmpeg_the_third::{Frame, decoder, packet::Packet};
|
||||||
@@ -72,7 +71,7 @@ impl Decoder for FfmpegDecoder {
|
|||||||
&mut self,
|
&mut self,
|
||||||
to_decode: FrameBuffer,
|
to_decode: FrameBuffer,
|
||||||
mut buffer: impl AsMut<[u8]>,
|
mut buffer: impl AsMut<[u8]>,
|
||||||
_destination_format: Option<Self::DestinationFormatHint>
|
_destination_format: Option<Self::DestinationFormatHint>,
|
||||||
) -> Result<Self::OutputMeta, NokhwaError> {
|
) -> Result<Self::OutputMeta, NokhwaError> {
|
||||||
// TODO: add an extra zippy happy path for rgb/bgr/luma
|
// TODO: add an extra zippy happy path for rgb/bgr/luma
|
||||||
let (frame, metadata) = self.receive_decoded_frame(to_decode)?;
|
let (frame, metadata) = self.receive_decoded_frame(to_decode)?;
|
||||||
@@ -108,19 +107,20 @@ impl Decoder for FfmpegDecoder {
|
|||||||
where
|
where
|
||||||
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
{
|
{
|
||||||
let destination_format =
|
let destination_format = pixel_to_destination_px_fmt::<P>().ok_or(
|
||||||
pixel_to_destination_px_fmt::<P>()
|
NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()),
|
||||||
.ok_or(NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()))?;
|
)?;
|
||||||
|
|
||||||
let buffer = buffer.as_mut();
|
let buffer = buffer.as_mut();
|
||||||
let estimated_size =
|
let estimated_size = self.codec.preferred_buffer_min_size(&None)?.ok_or(
|
||||||
self.codec
|
NokhwaError::DecoderInvalidBuffer(
|
||||||
.preferred_buffer_min_size(&None)?
|
"failed to estimate decoder buffer.length".to_string(),
|
||||||
.ok_or(NokhwaError::DecoderInvalidBuffer(
|
),
|
||||||
"failed to estimate decoder buffer.length".to_string(),
|
)?;
|
||||||
))?;
|
|
||||||
if buffer.len() < estimated_size {
|
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)?;
|
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::<P>(self.config().resolution);
|
let min_size = self.output_decoder_min_size_pixel::<P>(self.config().resolution);
|
||||||
let mut buffer: Vec<P::Subpixel> = vec![<P::Subpixel>::DEFAULT_MIN_VALUE; min_size];
|
let mut buffer: Vec<P::Subpixel> = vec![<P::Subpixel>::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(
|
Ok(DecodedImage::new(
|
||||||
ImageBuffer::from_vec(
|
ImageBuffer::from_vec(
|
||||||
self.codec.config.resolution.width(),
|
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 {
|
fn output_decoder_min_size(
|
||||||
let size =
|
&self,
|
||||||
unsafe { av_image_get_buffer_size(destination_format, resolution.width() as i32, resolution.height() as i32, 1) };
|
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
|
size as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,11 +368,13 @@ pub struct FfmpegCodec {
|
|||||||
|
|
||||||
impl FfmpegCodec {
|
impl FfmpegCodec {
|
||||||
fn new(config: <FfmpegCodec as Codec>::Config) -> Result<Self, NokhwaError> {
|
fn new(config: <FfmpegCodec as Codec>::Config) -> Result<Self, NokhwaError> {
|
||||||
let id = convert_format_to_codec_id(&config.frame_format)
|
let id = convert_format_to_codec_id(&config.frame_format).ok_or(
|
||||||
.ok_or(NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format))?;
|
NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format),
|
||||||
|
)?;
|
||||||
|
|
||||||
let codec =
|
let codec = decoder::find(id).ok_or(NokhwaError::DecoderInitializationError(
|
||||||
decoder::find(id).ok_or(NokhwaError::DecoderInitializationError("Failed to find codec".to_string()))?;
|
"Failed to find codec".to_string(),
|
||||||
|
))?;
|
||||||
|
|
||||||
let context = unsafe {
|
let context = unsafe {
|
||||||
let ptr = ffmpeg_the_third::ffi::avcodec_alloc_context3(codec.as_ptr());
|
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()))?;
|
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
|
||||||
video
|
video
|
||||||
.set_parameters(unsafe {
|
.set_parameters(unsafe {
|
||||||
Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::DecoderInitializationError(
|
Parameters::from_raw(config.as_ptr()?).ok_or(
|
||||||
"Failed to convert parameters".to_string(),
|
NokhwaError::DecoderInitializationError(
|
||||||
))?
|
"Failed to convert parameters".to_string(),
|
||||||
|
),
|
||||||
|
)?
|
||||||
})
|
})
|
||||||
.map_err(|why| NokhwaError::Decoder(why.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()?;
|
let mut temp_config = config.as_avcodec_params()?;
|
||||||
self.decoder
|
self.decoder
|
||||||
.set_parameters(unsafe {
|
.set_parameters(unsafe {
|
||||||
Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::DecoderInvalidConfiguration(
|
Parameters::from_raw(&mut temp_config).ok_or(
|
||||||
"Failed to convert parameters".to_string(),
|
NokhwaError::DecoderInvalidConfiguration(
|
||||||
))?
|
"Failed to convert parameters".to_string(),
|
||||||
|
),
|
||||||
|
)?
|
||||||
})
|
})
|
||||||
.map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?;
|
.map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?;
|
||||||
self.config = config;
|
self.config = config;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
extern crate core;
|
||||||
|
|
||||||
#[cfg(feature = "ffmpeg")]
|
#[cfg(feature = "ffmpeg")]
|
||||||
pub mod ffmpeg;
|
pub mod ffmpeg;
|
||||||
#[cfg(feature = "mjpeg")]
|
#[cfg(feature = "mjpeg")]
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
use zune_core::bytestream::ZCursor;
|
use bytemuck::cast_slice_mut;
|
||||||
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 nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel};
|
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel};
|
||||||
use nokhwa_core::error::NokhwaError;
|
use nokhwa_core::error::NokhwaError;
|
||||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
use nokhwa_core::frame_buffer::FrameBuffer;
|
||||||
|
use nokhwa_core::image::Primitive;
|
||||||
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
|
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
|
||||||
use nokhwa_core::types::Resolution;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MJpegDecoder {
|
pub struct MJpegDecoder {
|
||||||
@@ -30,7 +31,12 @@ impl Decoder for MJpegDecoder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option<Self::DestinationFormatHint>) -> Result<Self::OutputMeta, NokhwaError> {
|
fn decode_to_buffer(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer,
|
||||||
|
mut buffer: impl AsMut<[u8]>,
|
||||||
|
destination_format_hint: Option<Self::DestinationFormatHint>,
|
||||||
|
) -> Result<Self::OutputMeta, NokhwaError> {
|
||||||
let buffer = buffer.as_mut();
|
let buffer = buffer.as_mut();
|
||||||
let cursor = ZCursor::new(to_decode.as_ref());
|
let cursor = ZCursor::new(to_decode.as_ref());
|
||||||
|
|
||||||
@@ -38,7 +44,10 @@ impl Decoder for MJpegDecoder {
|
|||||||
|
|
||||||
let mut config = self.config.decoder_options;
|
let mut config = self.config.decoder_options;
|
||||||
if let Some(dest_hint) = destination_format_hint {
|
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);
|
decoder.set_options(config);
|
||||||
@@ -47,39 +56,64 @@ impl Decoder for MJpegDecoder {
|
|||||||
let info = match decoder.info() {
|
let info = match decoder.info() {
|
||||||
Some(i) => i,
|
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())
|
Ok(info.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_to_pixel_buffer<P: Pixel>(&mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError>
|
fn decode_to_pixel_buffer<P: Pixel>(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer,
|
||||||
|
mut buffer: impl AsMut<[P::Subpixel]>,
|
||||||
|
) -> Result<Self::OutputMeta, NokhwaError>
|
||||||
where
|
where
|
||||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
{
|
{
|
||||||
let hint = match pixel_to_colorspace::<P>() {
|
let hint = match pixel_to_colorspace::<P>() {
|
||||||
Some(cs) => cs,
|
Some(cs) => cs,
|
||||||
None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES))
|
None => {
|
||||||
|
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(
|
||||||
|
P::COLOR_MODEL,
|
||||||
|
<<P as Pixel>::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)
|
Ok(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode<P: Pixel>(&mut self, to_decode: FrameBuffer) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
|
fn decode<P: Pixel>(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer,
|
||||||
|
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
|
||||||
where
|
where
|
||||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
{
|
{
|
||||||
let min_size = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
|
let min_size = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
|
||||||
let mut buffer = vec![0_u8; min_size];
|
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MAX_VALUE; min_size];
|
||||||
let output_metadata = self.decode_to_pixel_buffer(to_decode, buffer.as_mut_slice())?;
|
let output_metadata =
|
||||||
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()))?;
|
self.decode_to_pixel_buffer::<P>(to_decode, out_buffer.as_mut_slice())?;
|
||||||
Ok(DecodedImage::new(
|
let image_buffer = ImageBuffer::from_raw(
|
||||||
image_buffer,
|
output_metadata.resolution.width(),
|
||||||
output_metadata
|
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 {
|
let stride = match destination_format {
|
||||||
OutputColor::Rgb => 3,
|
OutputColor::Rgb => 3,
|
||||||
OutputColor::RgbA => 4,
|
OutputColor::RgbA => 4,
|
||||||
@@ -122,7 +156,7 @@ impl From<ImageInfo> for ImageMeta {
|
|||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct MJpegOptions {
|
pub struct MJpegOptions {
|
||||||
pub resolution: Resolution,
|
pub resolution: Resolution,
|
||||||
pub decoder_options: DecoderOptions
|
pub decoder_options: DecoderOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
@@ -135,9 +169,9 @@ pub enum OutputColor {
|
|||||||
LumaA,
|
LumaA,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<ColorSpace> for OutputColor {
|
impl From<OutputColor> for ColorSpace {
|
||||||
fn into(self) -> ColorSpace {
|
fn from(val: OutputColor) -> Self {
|
||||||
match self {
|
match val {
|
||||||
OutputColor::Rgb => ColorSpace::RGB,
|
OutputColor::Rgb => ColorSpace::RGB,
|
||||||
OutputColor::RgbA => ColorSpace::RGBA,
|
OutputColor::RgbA => ColorSpace::RGBA,
|
||||||
OutputColor::Bgr => ColorSpace::BGR,
|
OutputColor::Bgr => ColorSpace::BGR,
|
||||||
@@ -148,7 +182,11 @@ impl Into<ColorSpace> for OutputColor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_to_colorspace<P>() -> Option<OutputColor> where P: Pixel, <P as Pixel>::Subpixel: NonFloatScalarWidth {
|
fn pixel_to_colorspace<P>() -> Option<OutputColor>
|
||||||
|
where
|
||||||
|
P: Pixel,
|
||||||
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
|
{
|
||||||
match P::COLOR_MODEL {
|
match P::COLOR_MODEL {
|
||||||
"RGBA" => Some(OutputColor::RgbA),
|
"RGBA" => Some(OutputColor::RgbA),
|
||||||
"RGB" => Some(OutputColor::Rgb),
|
"RGB" => Some(OutputColor::Rgb),
|
||||||
@@ -162,12 +200,8 @@ fn pixel_to_colorspace<P>() -> Option<OutputColor> where P: Pixel, <P as Pixel>:
|
|||||||
|
|
||||||
fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
||||||
match decode_errors {
|
match decode_errors {
|
||||||
DecodeErrors::Format(fmt) => {
|
DecodeErrors::Format(fmt) => NokhwaError::Decoder(fmt),
|
||||||
NokhwaError::Decoder(fmt)
|
DecodeErrors::FormatStatic(fmt) => NokhwaError::Decoder(fmt.to_string()),
|
||||||
}
|
|
||||||
DecodeErrors::FormatStatic(fmt) => {
|
|
||||||
NokhwaError::Decoder(fmt.to_string())
|
|
||||||
}
|
|
||||||
DecodeErrors::IllegalMagicBytes(b) => {
|
DecodeErrors::IllegalMagicBytes(b) => {
|
||||||
NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}"))
|
NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}"))
|
||||||
}
|
}
|
||||||
@@ -177,9 +211,10 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
|||||||
DecodeErrors::ZeroError => {
|
DecodeErrors::ZeroError => {
|
||||||
NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string())
|
NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string())
|
||||||
}
|
}
|
||||||
DecodeErrors::DqtError(e) | DecodeErrors::MCUError(e) | DecodeErrors::SosError(e) | DecodeErrors::SofError(e)=> {
|
DecodeErrors::DqtError(e)
|
||||||
NokhwaError::Decoder(format!("error decoding: {e}"))
|
| DecodeErrors::MCUError(e)
|
||||||
}
|
| DecodeErrors::SosError(e)
|
||||||
|
| DecodeErrors::SofError(e) => NokhwaError::Decoder(format!("error decoding: {e}")),
|
||||||
DecodeErrors::Unsupported(why) => {
|
DecodeErrors::Unsupported(why) => {
|
||||||
NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}"))
|
NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}"))
|
||||||
}
|
}
|
||||||
@@ -192,8 +227,6 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
|||||||
DecodeErrors::TooSmallOutput(a, b) => {
|
DecodeErrors::TooSmallOutput(a, b) => {
|
||||||
NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}"))
|
NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}"))
|
||||||
}
|
}
|
||||||
DecodeErrors::IoErrors(io) => {
|
DecodeErrors::IoErrors(io) => NokhwaError::Decoder(format!("io error: {io:?}")),
|
||||||
NokhwaError::Decoder(format!("io error: {io:?}"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+475
-168
@@ -1,15 +1,35 @@
|
|||||||
use bytemuck::{cast_slice, cast_slice_mut, try_cast_slice_mut};
|
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::error::NokhwaError;
|
||||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
use nokhwa_core::frame_buffer::FrameBuffer;
|
||||||
use nokhwa_core::frame_format::FrameFormat;
|
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::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 {
|
pub struct YUVDecoder {
|
||||||
config: YUVConfig,
|
config: YUVConfig,
|
||||||
stride_cache: Option<CachedStride>,
|
}
|
||||||
|
|
||||||
|
impl YUVDecoder {
|
||||||
|
pub fn new(config: <Self as Decoder>::Config) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for YUVDecoder {
|
impl Decoder for YUVDecoder {
|
||||||
@@ -23,85 +43,88 @@ impl Decoder for YUVDecoder {
|
|||||||
|
|
||||||
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
|
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
|
||||||
if !FrameFormat::YCBCR.contains(&config.yuv_type) {
|
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.config = config;
|
||||||
self.stride_cache = None;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_to_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, destination_format: Option<Self::DestinationFormatHint>) -> Result<Self::OutputMeta, NokhwaError> {
|
fn decode_to_buffer(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer<'_>,
|
||||||
|
mut buffer: impl AsMut<[u8]>,
|
||||||
|
destination_format: Option<Self::DestinationFormatHint>,
|
||||||
|
) -> Result<Self::OutputMeta, NokhwaError> {
|
||||||
let destination_format = match destination_format {
|
let destination_format = match destination_format {
|
||||||
Some(df) => df,
|
Some(df) => df,
|
||||||
None => return Err(NokhwaError::DecoderDestinationHintRequired)
|
None => return Err(NokhwaError::DecoderDestinationHintRequired),
|
||||||
};
|
};
|
||||||
|
|
||||||
let buffer = buffer.as_mut();
|
let buffer = buffer.as_mut();
|
||||||
if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format) {
|
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 {
|
let stride = figure_out_stride(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?;
|
||||||
Some(c) => c,
|
let byte_width = figure_out_byte_width(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?;
|
||||||
None => {
|
|
||||||
self.stride_cache = figure_out_stride(self.config.yuv_type);
|
|
||||||
match self.stride_cache {
|
let stride_3px = 3 *
|
||||||
Some(s) => s,
|
self.config.resolution.width();
|
||||||
None => return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)),
|
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 </3
|
// todo: clean up ts into a macro </3
|
||||||
let decode_status = match stride {
|
let decode_status = match stride {
|
||||||
CachedStride::Packed(stride) => {
|
Stride::Packed(stride) => {
|
||||||
let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride);
|
let image = prepare_to_packed_image(&to_decode, self.config.resolution, byte_width, stride);
|
||||||
match self.config.yuv_type {
|
match self.config.yuv_type {
|
||||||
FrameFormat::Ayuv_32 => {
|
FrameFormat::Ayuv_32 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::Yuyv_4_2_2 => {
|
FrameFormat::Yuyv_4_2_2 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, 3, 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, 4, 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), 6, 16, 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), 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), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||||
YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, 8, 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, 4, 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 => {
|
FrameFormat::Uyvy_4_2_2 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, 3, 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, 4, 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), 6, 16, 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), 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), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||||
YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, 3, 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, 4, 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 => {
|
FrameFormat::Vyuy_4_2_2 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, 3, 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, 4, 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), 6, 16, 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), 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), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||||
YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, 3, 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, 4, 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 => {
|
FrameFormat::Yvyu_4_2_2 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, 3, 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, 4, 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), 6, 16, 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), 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), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||||
YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, 3, 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, 4, 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) => {
|
Stride::Semi(y_stride, uv_stride) => {
|
||||||
let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, 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 {
|
match self.config.yuv_type {
|
||||||
FrameFormat::NV24 => {
|
FrameFormat::NV24 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::NV42 => {
|
FrameFormat::NV42 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::NV16 => {
|
FrameFormat::NV16 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::NV61 => {
|
FrameFormat::NV61 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::NV12 => {
|
FrameFormat::NV12 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::NV21 => {
|
FrameFormat::NV21 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::P010 => {
|
FrameFormat::P010 => {
|
||||||
|
let a = convert_bi_planar_image_to_u16(image);
|
||||||
match destination_format {
|
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::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, 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::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, 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::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, 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::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, 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::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(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, 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,
|
// _ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrameFormat::P012 => {
|
FrameFormat::P012 => {
|
||||||
match destination_format {
|
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::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), 8, 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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,15 +221,15 @@ impl Decoder for YUVDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CachedStride::Planar(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, y_stride, u_stride, v_stride);
|
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 {
|
match self.config.yuv_type {
|
||||||
FrameFormat::Yuv_4_2_0 => {
|
FrameFormat::Yuv_4_2_0 => {
|
||||||
match destination_format {
|
match destination_format {
|
||||||
YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, 3, 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, 4, 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, 3, 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, 4, self.config.range, self.config.matrix)),
|
YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,46 +246,68 @@ impl Decoder for YUVDecoder {
|
|||||||
match decode_status {
|
match decode_status {
|
||||||
Some(Ok(_)) => Ok(()),
|
Some(Ok(_)) => Ok(()),
|
||||||
Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())),
|
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<P: Pixel>(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError>
|
fn decode_to_pixel_buffer<P: Pixel>(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer<'_>,
|
||||||
|
mut buffer: impl AsMut<[P::Subpixel]>,
|
||||||
|
) -> Result<Self::OutputMeta, NokhwaError>
|
||||||
where
|
where
|
||||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
{
|
{
|
||||||
let destination = match YUVDestination::get_by_pixel::<P>() {
|
let destination = match YUVDestination::get_by_pixel::<P>() {
|
||||||
None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)),
|
None => {
|
||||||
|
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(
|
||||||
|
P::COLOR_MODEL,
|
||||||
|
<<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES,
|
||||||
|
));
|
||||||
|
}
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
};
|
};
|
||||||
let buffer = buffer.as_mut();
|
let buffer = buffer.as_mut();
|
||||||
|
|
||||||
let cast_slice = try_cast_slice_mut::<P::Subpixel, u8>(buffer)
|
let cast_slice = try_cast_slice_mut::<P::Subpixel, u8>(buffer)
|
||||||
.map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?;
|
.map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?;
|
||||||
|
|
||||||
self.decode_to_buffer(to_decode, cast_slice, Some(destination))
|
self.decode_to_buffer(to_decode, cast_slice, Some(destination))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode<P: Pixel>(&mut self, to_decode: FrameBuffer<'_>) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
|
fn decode<P: Pixel>(
|
||||||
|
&mut self,
|
||||||
|
to_decode: FrameBuffer<'_>,
|
||||||
|
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
|
||||||
where
|
where
|
||||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
{
|
{
|
||||||
let min_size_alloc = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
|
let min_size_alloc = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
|
||||||
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc];
|
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc];
|
||||||
self.decode_to_pixel_buffer::<P>(to_decode, &mut out_buffer)?;
|
self.decode_to_pixel_buffer::<P>(to_decode, &mut out_buffer)?;
|
||||||
Ok(
|
Ok(DecodedImage::new(
|
||||||
DecodedImage::new(
|
ImageBuffer::from_vec(
|
||||||
ImageBuffer::from_vec(self.config.resolution.width(), self.config.resolution.height(), out_buffer)
|
self.config.resolution.width(),
|
||||||
.ok_or(NokhwaError::Decoder("failed to convert into an image buffer".to_string()))?,
|
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 {
|
let px_size = match destination_format {
|
||||||
YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3,
|
YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3,
|
||||||
YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4,
|
YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4,
|
||||||
YUVDestination::Rgb16 => 3 * 2,
|
YUVDestination::Rgb16 => 3 * 2,
|
||||||
YUVDestination::Rgba16 => 4 * 2,
|
YUVDestination::Rgba16 => 4 * 2,
|
||||||
};
|
};
|
||||||
@@ -285,30 +331,32 @@ pub enum YUVDestination {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl YUVDestination {
|
impl YUVDestination {
|
||||||
pub fn get_by_pixel<P>() -> Option<Self> where P: Pixel, <P as Pixel>::Subpixel: NonFloatScalarWidth {
|
pub fn get_by_pixel<P>() -> Option<Self>
|
||||||
|
where
|
||||||
|
P: Pixel,
|
||||||
|
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||||
|
{
|
||||||
match P::COLOR_MODEL {
|
match P::COLOR_MODEL {
|
||||||
"RGB" => {
|
"RGB" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||||
match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
1 => Some(YUVDestination::Rgb8),
|
||||||
1 => Some(YUVDestination::Rgb8),
|
2 => Some(YUVDestination::Rgb16),
|
||||||
2 => Some(YUVDestination::Rgb16),
|
_ => None,
|
||||||
_ => None,
|
},
|
||||||
}
|
"RGBA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||||
}
|
|
||||||
"RGBA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
|
||||||
1 => Some(YUVDestination::Rgba8),
|
1 => Some(YUVDestination::Rgba8),
|
||||||
2 => Some(YUVDestination::Rgba16),
|
2 => Some(YUVDestination::Rgba16),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
},
|
||||||
"BGR" =>match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
"BGR" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||||
1 => Some(YUVDestination::Bgr8),
|
1 => Some(YUVDestination::Bgr8),
|
||||||
// 2 => Some(YUVDestination::Bgr16),
|
// 2 => Some(YUVDestination::Bgr16),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
},
|
||||||
"BGRA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
"BGRA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||||
1 => Some(YUVDestination::Bgra8),
|
1 => Some(YUVDestination::Bgra8),
|
||||||
// 2 => Some(YUVDestination::Bgra16),
|
// 2 => Some(YUVDestination::Bgra16),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +377,7 @@ impl TryFrom<CameraFormat> for YUVConfig {
|
|||||||
|
|
||||||
fn try_from(value: CameraFormat) -> Result<Self, Self::Error> {
|
fn try_from(value: CameraFormat) -> Result<Self, Self::Error> {
|
||||||
if !FrameFormat::YCBCR.contains(&value.format()) {
|
if !FrameFormat::YCBCR.contains(&value.format()) {
|
||||||
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()))
|
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()));
|
||||||
}
|
}
|
||||||
Ok(YUVConfig {
|
Ok(YUVConfig {
|
||||||
resolution: value.resolution(),
|
resolution: value.resolution(),
|
||||||
@@ -342,33 +390,69 @@ impl TryFrom<CameraFormat> for YUVConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum CachedStride {
|
enum Stride {
|
||||||
Packed(u32),
|
Packed(u32),
|
||||||
SemiPlanar(u32, u32),
|
Semi(u32, u32),
|
||||||
Planar(u32, u32, u32),
|
Planar(u32, u32, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn figure_out_stride(frame_format: FrameFormat) -> Option<CachedStride> {
|
fn figure_out_stride(frame_format: FrameFormat) -> Option<Stride> {
|
||||||
if let Some(s) = packed_stride_component_per_4px(frame_format) {
|
if let Some(yuy) = packed_stride_component(frame_format) {
|
||||||
return Some(CachedStride::Packed(s));
|
return Some(Stride::Packed(yuy));
|
||||||
}
|
}
|
||||||
if let Some((s1, s2)) = semiplanar_stride_per_4px(frame_format) {
|
if let Some((y, uv)) = semiplanar_stride(frame_format) {
|
||||||
return Some(CachedStride::SemiPlanar(s1, s2));
|
return Some(Stride::Semi(y, uv));
|
||||||
}
|
}
|
||||||
if let Some((s1, s2, s3)) = planar_stride_per_4px(frame_format) {
|
// l here means the ratio of luma lines to chroma lines
|
||||||
return Some(CachedStride::Planar(s1, s2, s3));
|
// 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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn packed_stride_component_per_4px(format: FrameFormat) -> Option<u32> {
|
fn figure_out_byte_width(format: FrameFormat) -> Option<u32> {
|
||||||
match format {
|
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<u32> {
|
||||||
|
match format {
|
||||||
|
FrameFormat::Ayuv_32 => Some(4),
|
||||||
FrameFormat::Yuyv_4_2_2
|
FrameFormat::Yuyv_4_2_2
|
||||||
| FrameFormat::Uyvy_4_2_2
|
| FrameFormat::Uyvy_4_2_2
|
||||||
| FrameFormat::Vyuy_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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,77 +460,79 @@ fn packed_stride_component_per_4px(format: FrameFormat) -> Option<u32> {
|
|||||||
fn prepare_to_packed_image<'a>(
|
fn prepare_to_packed_image<'a>(
|
||||||
frame_buffer: &'a FrameBuffer<'a>,
|
frame_buffer: &'a FrameBuffer<'a>,
|
||||||
resolution: Resolution,
|
resolution: Resolution,
|
||||||
|
byte_width: u32,
|
||||||
yuy_stride: u32,
|
yuy_stride: u32,
|
||||||
) -> YuvPackedImage<'a, u8> {
|
) -> YuvPackedImage<'a, u8> {
|
||||||
YuvPackedImage {
|
YuvPackedImage {
|
||||||
yuy: frame_buffer.buffer(),
|
yuy: frame_buffer.buffer(),
|
||||||
yuy_stride,
|
yuy_stride: yuy_stride * resolution.width(),
|
||||||
width: resolution.width(),
|
width: resolution.width(),
|
||||||
height: resolution.height(),
|
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>(
|
fn prepare_to_semi_planar_image<'a>(
|
||||||
frame_buffer: &'a FrameBuffer<'a>,
|
frame_buffer: &'a FrameBuffer<'a>,
|
||||||
resolution: Resolution,
|
resolution: Resolution,
|
||||||
|
byte_width: u32,
|
||||||
y_stride: u32,
|
y_stride: u32,
|
||||||
uv_stride: u32,
|
uv_stride: u32,
|
||||||
) -> YuvBiPlanarImage<'a, u8> {
|
) -> YuvBiPlanarImage<'a, u8> {
|
||||||
let uv_start = (resolution.width() * resolution.height()) as usize;
|
let uv_start = (resolution.width() * resolution.height() * byte_width) as usize;
|
||||||
YuvBiPlanarImage {
|
YuvBiPlanarImage {
|
||||||
y_plane: &frame_buffer.buffer()[0..uv_start],
|
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_plane: &frame_buffer.buffer()[uv_start..frame_buffer.len()],
|
||||||
uv_stride,
|
uv_stride: uv_stride * resolution.width(),
|
||||||
width: resolution.width(),
|
width: resolution.width(),
|
||||||
height: resolution.height(),
|
height: resolution.height(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> {
|
// fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> {
|
||||||
match format {
|
// match format {
|
||||||
FrameFormat::Yuv_4_2_0 => Some((4, 1, 1)),
|
// FrameFormat::Yuv_4_2_0 => Some((stride_ 1, 1)),
|
||||||
_ => None,
|
// _ => None,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// does this work? idk
|
||||||
fn prepare_to_planar_image<'a>(
|
fn prepare_to_planar_image<'a>(
|
||||||
frame_buffer: &'a FrameBuffer<'a>,
|
frame_buffer: &'a FrameBuffer<'a>,
|
||||||
resolution: Resolution,
|
resolution: Resolution,
|
||||||
y_stride: u32,
|
byte_width: u32,
|
||||||
u_stride: u32,
|
y_stride_base: u32,
|
||||||
v_stride: u32,
|
u_stride_ratio: u32,
|
||||||
|
v_stride_ratio: u32,
|
||||||
|
line_ratio: u32,
|
||||||
) -> YuvPlanarImage<'a, u8> {
|
) -> YuvPlanarImage<'a, u8> {
|
||||||
let u_start = (resolution.width() * resolution.height()) as usize;
|
|
||||||
let v_start =
|
let y_stride = resolution.width() * y_stride_base;
|
||||||
u_start + ((resolution.width() / 4) * y_stride * (resolution.height() * u_stride)) as usize;
|
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 {
|
YuvPlanarImage {
|
||||||
y_plane: &frame_buffer.buffer()[0..u_start],
|
y_plane: &frame_buffer.buffer()[0..us],
|
||||||
y_stride,
|
y_stride,
|
||||||
u_plane: &frame_buffer.buffer()[u_start..v_start],
|
u_plane: &frame_buffer.buffer()[us..vs],
|
||||||
u_stride,
|
u_stride,
|
||||||
v_plane: &frame_buffer.buffer()[v_start..frame_buffer.len()],
|
v_plane: &frame_buffer.buffer()[vs..frame_buffer.len()],
|
||||||
v_stride,
|
v_stride,
|
||||||
width: resolution.width(),
|
width: resolution.width(),
|
||||||
height: resolution.height(),
|
height: resolution.height(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_packed_image_to_u16(
|
fn convert_packed_image_to_u16(yuv_packed_image: YuvPackedImage<u8>) -> YuvPackedImage<u16> {
|
||||||
yuv_packed_image: YuvPackedImage<u8>,
|
let buf = cast_slice(yuv_packed_image.yuy);
|
||||||
) -> YuvPackedImage<u16> {
|
|
||||||
let buf= cast_slice(yuv_packed_image.yuy);
|
|
||||||
YuvPackedImage {
|
YuvPackedImage {
|
||||||
yuy: buf,
|
yuy: buf,
|
||||||
yuy_stride: yuv_packed_image.yuy_stride,
|
yuy_stride: yuv_packed_image.yuy_stride,
|
||||||
@@ -458,8 +544,8 @@ fn convert_packed_image_to_u16(
|
|||||||
fn convert_bi_planar_image_to_u16(
|
fn convert_bi_planar_image_to_u16(
|
||||||
yuv_bi_planar_image: YuvBiPlanarImage<u8>,
|
yuv_bi_planar_image: YuvBiPlanarImage<u8>,
|
||||||
) -> YuvBiPlanarImage<u16> {
|
) -> YuvBiPlanarImage<u16> {
|
||||||
let buf_y= cast_slice(yuv_bi_planar_image.y_plane);
|
let buf_y = cast_slice(yuv_bi_planar_image.y_plane);
|
||||||
let buf_uv= cast_slice(yuv_bi_planar_image.uv_plane);
|
let buf_uv = cast_slice(yuv_bi_planar_image.uv_plane);
|
||||||
YuvBiPlanarImage {
|
YuvBiPlanarImage {
|
||||||
y_plane: buf_y,
|
y_plane: buf_y,
|
||||||
y_stride: yuv_bi_planar_image.y_stride,
|
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,
|
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<P: Pixel>(filename: String, resolution: Resolution, format: FrameFormat) -> ImageBuffer<P, Vec<P::Subpixel>>
|
||||||
|
where
|
||||||
|
<P as Pixel>::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::<P>(frame_buffer).unwrap().buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_image<P: Pixel + PixelWithColorType>(image: ImageBuffer<P, Vec<P::Subpixel>>, filename: String) where
|
||||||
|
[<P as Pixel>::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::<Rgb<u8>>(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::<Rgba<u8>>(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::<Rgb<u8>>(format!("{base_filename}.yuv"), resolution, format);
|
||||||
|
//
|
||||||
|
// write_image(out, format!("{base_filename}.rgb8.png"))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let out = base_test::<Rgba<u8>>(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::<Rgb<u8>>(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::<Rgba<u8>>(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::<Rgb<u8>>(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::<Rgba<u8>>(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::<Rgb<u8>>(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::<Rgba<u8>>(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::<Rgb<u16>>(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::<Rgb<u8>>(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::<Rgba<u8>>(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::<Rgb<u8>>(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::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
|
||||||
|
assert_eq!(image_rgba8.as_raw(), out.as_raw());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
-158
@@ -1,158 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / 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<dyn CaptureTrait + Backend>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Camera {
|
|
||||||
pub fn new() -> Result<Self, NokhwaError> {}
|
|
||||||
|
|
||||||
pub fn with_api_backend() -> Result<Self, NokhwaError> {}
|
|
||||||
|
|
||||||
pub fn with_custom_backend() -> Result<Self, NokhwaError> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CaptureTrait for Camera {
|
|
||||||
fn init(&mut self) -> Result<(), NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_with_format(&mut self, format: FormatFilter) -> Result<CameraFormat, NokhwaError> {
|
|
||||||
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<CameraFormat> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compatible_list_by_resolution(
|
|
||||||
&mut self,
|
|
||||||
fourcc: SourceFrameFormat,
|
|
||||||
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compatible_fourcc(&mut self) -> Result<Vec<SourceFrameFormat>, NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolution(&self) -> Option<Resolution> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame_rate(&self) -> Option<u32> {
|
|
||||||
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<SourceFrameFormat>,
|
|
||||||
) -> Result<(), NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn camera_controls(&self) -> Result<Vec<CameraControl>, 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<FrameBuffer, NokhwaError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame_raw(&mut self) -> Result<Cow<[u8]>, 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 {}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user