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]
|
||||
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "nokhwa-decoders"]
|
||||
exclude = ["examples/*"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[features]
|
||||
default = ["decoding-yuv","decoding-mozjpeg"]
|
||||
default = ["decoding-yuv","decoding-mjpeg"]
|
||||
serialize = ["serde", "nokhwa-core/serialize"]
|
||||
decoding-yuv = ["mozjpeg"]
|
||||
decoding-mozjpeg = ["mozjpeg"]
|
||||
decoding-yuv = ["nokhwa-decoders/yuv"]
|
||||
decoding-mjpeg = ["nokhwa-decoders/mjpeg"]
|
||||
decoding-ffmpeg = ["nokhwa-decoders/ffmpeg"]
|
||||
input-avfoundation = ["nokhwa-bindings-macos", "flume"]
|
||||
input-msmf = ["nokhwa-bindings-windows"]
|
||||
input-v4l = ["nokhwa-bindings-linux"]
|
||||
@@ -31,7 +31,7 @@ input-native = ["input-avfoundation", "input-v4l", "input-msmf"]
|
||||
# input-uvc = ["uvc", "uvc/vendor", "usb_enumeration", "lazy_static"]
|
||||
input-opencv = ["opencv", "opencv/rgb", "rgb", "nokhwa-core/opencv-mat"]
|
||||
input-jscam = [ "wasm-bindgen-futures", "wasm-rs-async-executor", "output-async", "js-sys", "web-sys", "serde-wasm-bindgen", "serde"]
|
||||
output-wgpu = ["wgpu", "nokhwa-core/wgpu-types"]
|
||||
output-wgpu = ["wgpu-types", "nokhwa-core/wgpu"]
|
||||
#output-wasm = ["input-jscam"]
|
||||
output-threaded = []
|
||||
output-async = ["nokhwa-core/async", "async-trait"]
|
||||
@@ -43,44 +43,10 @@ test-fail-warning = []
|
||||
[dependencies]
|
||||
paste = "1.0"
|
||||
|
||||
[dependencies.mozjpeg]
|
||||
version = "0.10"
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-core]
|
||||
version = "0.2"
|
||||
path = "nokhwa-core"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.flume]
|
||||
version = "0.11"
|
||||
optional = true
|
||||
|
||||
[dependencies.image]
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
|
||||
[dependencies.usb_enumeration]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu]
|
||||
version = "25"
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
version = "0.94"
|
||||
default-features = false
|
||||
features = ["videoio"]
|
||||
optional = true
|
||||
|
||||
[dependencies.rgb]
|
||||
version = "0.8"
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-bindings-windows]
|
||||
version = "0.4"
|
||||
path = "nokhwa-bindings-windows"
|
||||
@@ -96,6 +62,37 @@ version = "0.2"
|
||||
path = "nokhwa-bindings-linux"
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-decoders]
|
||||
path = "nokhwa-decoders"
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.flume]
|
||||
version = "0.11"
|
||||
optional = true
|
||||
|
||||
[dependencies.image]
|
||||
workspace = true
|
||||
|
||||
[dependencies.usb_enumeration]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu-types]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.rgb]
|
||||
version = "0.8"
|
||||
optional = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = [
|
||||
@@ -144,3 +141,18 @@ optional = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["docs-only", "docs-nolink", "docs-features"]
|
||||
|
||||
[workspace.dependencies.image]
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
|
||||
[workspace.dependencies.wgpu-types]
|
||||
version = "26"
|
||||
|
||||
[workspace.dependencies.opencv]
|
||||
version = "0.95"
|
||||
default-features = false
|
||||
|
||||
Generated
+6
-6
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1751498133,
|
||||
"narHash": "sha256-QWJ+NQbMU+NcU2xiyo7SNox1fAuwksGlQhpzBl76g1I=",
|
||||
"lastModified": 1755020227,
|
||||
"narHash": "sha256-gGmm+h0t6rY88RPTaIm3su95QvQIVjAJx558YUG4Id8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d55716bb59b91ae9d1ced4b1ccdea7a442ecbfdb",
|
||||
"rev": "695d5db1b8b20b73292501683a524e0bd79074fb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -62,11 +62,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751510438,
|
||||
"narHash": "sha256-m8PjOoyyCR4nhqtHEBP1tB/jF+gJYYguSZmUmVTEAQE=",
|
||||
"lastModified": 1755052812,
|
||||
"narHash": "sha256-Tjw2YP7Hz8+ibE8wJ+Ps65vh1lzAe5ozmoo9sdQ7rGg=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "7f415261f298656f8164bd636c0dc05af4e95b6b",
|
||||
"rev": "433023cba5f4fa66b8b0fdbb8f91d420c9cc2527",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
rust-overlay,
|
||||
@@ -13,27 +14,42 @@
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system: let
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ rust-overlay.overlays.default ];
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
rustbin = pkgs.rust-bin.selectLatestNightlyWith (toolchain:
|
||||
rustshell = pkgs.mkShell.override {
|
||||
stdenv = pkgs.gccStdenv;
|
||||
};
|
||||
rustbin = pkgs.rust-bin.selectLatestNightlyWith (
|
||||
toolchain:
|
||||
toolchain.default.override {
|
||||
extensions = ["rust-src" "clippy" "rustfmt" "miri"];
|
||||
});
|
||||
in {
|
||||
extensions = [
|
||||
"rust-src"
|
||||
"clippy"
|
||||
"rustfmt"
|
||||
"miri"
|
||||
"rust-analyzer"
|
||||
];
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
formatter = pkgs.alejandra;
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
devShells.default = rustshell {
|
||||
packages = [
|
||||
rustbin
|
||||
] ++ (with pkgs; [
|
||||
llvmPackages.libclang.lib
|
||||
llvmPackages.clang
|
||||
]
|
||||
++ (with pkgs; [
|
||||
llvmPackages_21.clangWithLibcAndBasicRtAndLibcxx
|
||||
pkg-config
|
||||
cmake
|
||||
vcpkg
|
||||
lldb
|
||||
rustPlatform.bindgenHook
|
||||
xmlstarlet
|
||||
opencv
|
||||
@@ -46,14 +62,38 @@
|
||||
libv4l
|
||||
pipewire
|
||||
rustup
|
||||
gcc
|
||||
ffmpeg-full
|
||||
nasm
|
||||
libGL
|
||||
flite
|
||||
quirc
|
||||
lcevcdec
|
||||
xz
|
||||
celt
|
||||
opencore-amr
|
||||
snappy
|
||||
codec2
|
||||
gsm
|
||||
ilbc
|
||||
lame
|
||||
libtheora
|
||||
libogg
|
||||
twolame
|
||||
vo-amrwbenc
|
||||
vvenc
|
||||
xavs
|
||||
xvidcore
|
||||
soxr
|
||||
libvdpau
|
||||
jetbrains.rust-rover
|
||||
]);
|
||||
|
||||
env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library";
|
||||
env.LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
|
||||
shellHook = let
|
||||
shellHook =
|
||||
let
|
||||
pathToRustProject = "/project/component[@name='RustProjectSettings']";
|
||||
in
|
||||
''
|
||||
|
||||
@@ -29,7 +29,6 @@ typed-builder = "0.21"
|
||||
compact_str = "0.9"
|
||||
bytemuck = "1.23"
|
||||
smallmap = "1.4"
|
||||
constcat = "0.6"
|
||||
paste = "1.0"
|
||||
|
||||
[dependencies.num-rational]
|
||||
@@ -38,21 +37,18 @@ default-features = false
|
||||
features = ["serde", "std"]
|
||||
|
||||
[dependencies.image]
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
workspace = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu-types]
|
||||
version = "25"
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
version = "0.94"
|
||||
default-features = false
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[dependencies.async-trait]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_buffer::FrameBuffer;
|
||||
use crate::image::{DecodedImage, NonFloatScalarWidth};
|
||||
use crate::types::{CameraFormat, Resolution};
|
||||
use crate::types::{Resolution};
|
||||
pub use image::{ImageBuffer, Pixel, Primitive};
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::platform::Backends;
|
||||
use std::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
use crate::types::Backends;
|
||||
|
||||
pub type NokhwaResult<T> = Result<T, NokhwaError>;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use bytemuck::Pod;
|
||||
use image::{ImageBuffer, Pixel, Primitive};
|
||||
pub use image::{ImageBuffer, Pixel, Primitive};
|
||||
use num_traits::{NumCast, PrimInt};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
use crate::camera::Camera;
|
||||
use crate::error::NokhwaResult;
|
||||
use crate::types::{CameraIndex, CameraInformation};
|
||||
use crate::types::{Backends, CameraIndex, CameraInformation, QueriedCamera};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Backends {
|
||||
Video4Linux2,
|
||||
WebWASM,
|
||||
AVFoundation,
|
||||
MicrosoftMediaFoundation,
|
||||
OpenCV,
|
||||
Custom(&'static str),
|
||||
}
|
||||
|
||||
impl Display for Backends {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformTrait {
|
||||
const PLATFORM: Backends;
|
||||
@@ -27,7 +12,7 @@ pub trait PlatformTrait {
|
||||
|
||||
fn check_permission_given(&mut self) -> bool;
|
||||
|
||||
fn query(&mut self) -> NokhwaResult<Vec<CameraInformation>>;
|
||||
fn query(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
|
||||
|
||||
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 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>;
|
||||
|
||||
|
||||
+50
-140
@@ -44,8 +44,7 @@ impl CameraIndex {
|
||||
pub fn as_string(&self) -> String {
|
||||
match self {
|
||||
CameraIndex::Index(i) => i.to_string(),
|
||||
CameraIndex::String(s) => s.to_string(),
|
||||
CameraIndex::Stable(s) => s.to_string(),
|
||||
CameraIndex::String(s) | CameraIndex::Stable(s) => s.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +132,7 @@ impl Resolution {
|
||||
/// Get the x (width) of Resolution
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn x(self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
@@ -140,6 +140,7 @@ impl Resolution {
|
||||
/// Get the y (height) of Resolution
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[deprecated]
|
||||
pub fn y(self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
@@ -152,7 +153,7 @@ impl Resolution {
|
||||
|
||||
impl Display for Resolution {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}x{}", self.x(), self.y())
|
||||
write!(f, "{}x{}", self.width(), self.height())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +165,9 @@ impl PartialOrd for Resolution {
|
||||
|
||||
impl Ord for Resolution {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match self.x().cmp(&other.x()) {
|
||||
match self.width().cmp(&other.width()) {
|
||||
Ordering::Less => Ordering::Less,
|
||||
Ordering::Equal => self.y().cmp(&other.y()),
|
||||
Ordering::Equal => self.height().cmp(&other.height()),
|
||||
Ordering::Greater => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
@@ -174,11 +175,11 @@ impl Ord for Resolution {
|
||||
|
||||
impl Distance<u32> for Resolution {
|
||||
fn distance_from(&self, other: &Self) -> u32 {
|
||||
let x1 = self.x();
|
||||
let x2 = other.x();
|
||||
let x1 = self.width();
|
||||
let x2 = other.width();
|
||||
|
||||
let y1 = self.y();
|
||||
let y2 = other.y();
|
||||
let y1 = self.height();
|
||||
let y2 = other.height();
|
||||
|
||||
(x2 - x1).pow(2) + (y2 - y1).pow(2)
|
||||
}
|
||||
@@ -188,8 +189,8 @@ impl Div for Resolution {
|
||||
type Output = Resolution;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
let x_div = self.x().div(rhs.x());
|
||||
let y_div = self.y().div(rhs.y());
|
||||
let x_div = self.width().div(rhs.width());
|
||||
let y_div = self.height().div(rhs.height());
|
||||
Resolution::new(x_div, y_div)
|
||||
}
|
||||
}
|
||||
@@ -198,8 +199,8 @@ impl Sub for Resolution {
|
||||
type Output = Resolution;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
let x_sub = self.x().sub(rhs.x());
|
||||
let y_sub = self.y().sub(rhs.y());
|
||||
let x_sub = self.width().sub(rhs.width());
|
||||
let y_sub = self.height().sub(rhs.height());
|
||||
Resolution::new(x_sub, y_sub)
|
||||
}
|
||||
}
|
||||
@@ -208,8 +209,8 @@ impl Rem for Resolution {
|
||||
type Output = Resolution;
|
||||
|
||||
fn rem(self, rhs: Self) -> Self::Output {
|
||||
let x_rem = self.x().rem(rhs.x());
|
||||
let y_rem = self.y().rem(rhs.y());
|
||||
let x_rem = self.width().rem(rhs.width());
|
||||
let y_rem = self.height().rem(rhs.height());
|
||||
Resolution::new(x_rem, y_rem)
|
||||
}
|
||||
}
|
||||
@@ -429,15 +430,15 @@ impl Display for CameraFormat {
|
||||
}
|
||||
|
||||
/// Information about a Camera e.g. its name.
|
||||
/// `description` amd `misc` may contain information that may differ from backend to backend. Refer to each backend for details.
|
||||
/// `index` is a camera's index given to it by (usually) the OS usually in the order it is known to the system.
|
||||
/// `description` and `misc` may contain information that may differ from backend to backend. Refer to each backend for details.
|
||||
/// `stable_id` contains the stable ID that may be used to reopen the same device.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct CameraInformation {
|
||||
human_name: String,
|
||||
description: String,
|
||||
misc: String,
|
||||
index: CameraIndex,
|
||||
stable_id: Option<String>,
|
||||
}
|
||||
|
||||
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)` ????
|
||||
// I need to get on the same shit that my previous self was on, because holy shit that stuff is strong as FUCK!
|
||||
// Finally fixed this insanity. Hopefully I didnt torment anyone by actually putting this in a stable release.
|
||||
pub fn new(human_name: String, description: String, misc: String, index: CameraIndex) -> Self {
|
||||
pub fn new(human_name: String, description: String, misc: String, stable_id: Option<String>) -> Self {
|
||||
CameraInformation {
|
||||
human_name,
|
||||
description,
|
||||
misc,
|
||||
index,
|
||||
stable_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,146 +460,55 @@ impl CameraInformation {
|
||||
#[must_use]
|
||||
// yes, i know, unnecessary alloc this, unnecessary alloc that
|
||||
// but wasm bindgen
|
||||
pub fn human_name(&self) -> String {
|
||||
self.human_name.clone()
|
||||
}
|
||||
|
||||
/// Set the device info's human name.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `set_HumanReadableName`.
|
||||
pub fn set_human_name(&mut self, human_name: &str) {
|
||||
self.human_name = human_name.to_string();
|
||||
pub fn human_name(&self) -> &str {
|
||||
&self.human_name
|
||||
}
|
||||
|
||||
/// Get a reference to the device info's description.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `get_Description`.
|
||||
#[must_use]
|
||||
pub fn description(&self) -> &str {
|
||||
self.description.borrow()
|
||||
}
|
||||
|
||||
/// Set the device info's description.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `set_Description`.
|
||||
pub fn set_description(&mut self, description: &str) {
|
||||
self.description = description.to_string();
|
||||
&self.description
|
||||
}
|
||||
|
||||
/// Get a reference to the device info's misc.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `get_MiscString`.
|
||||
#[must_use]
|
||||
pub fn misc(&self) -> String {
|
||||
self.misc.clone()
|
||||
pub fn misc(&self) -> &str {
|
||||
&self.misc
|
||||
}
|
||||
|
||||
/// Set the device info's misc.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `set_MiscString`.
|
||||
pub fn set_misc(&mut self, misc: &str) {
|
||||
self.misc = misc.to_string();
|
||||
#[must_use] pub fn stable_id(&self) -> Option<&str> {
|
||||
self.stable_id.as_deref()
|
||||
}
|
||||
|
||||
/// Get a reference to the device info's index.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `get_Index`.
|
||||
#[must_use]
|
||||
pub fn index(&self) -> &CameraIndex {
|
||||
&self.index
|
||||
}
|
||||
|
||||
/// Set the device info's index.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `set_Index`.
|
||||
pub fn set_index(&mut self, index: CameraIndex) {
|
||||
self.index = index;
|
||||
}
|
||||
|
||||
// /// Gets the device info's index as an `u32`.
|
||||
// /// # Errors
|
||||
// /// If the index is not parsable as a `u32`, this will error.
|
||||
// /// # JS-WASM
|
||||
// /// This is exported as `get_Index_Int`
|
||||
// #[cfg_attr(feature = "output-wasm", wasm_bindgen(getter = Index_Int))]
|
||||
// pub fn index_num(&self) -> Result<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 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Name: {}, Description: {}, Extra: {}, Index: {}",
|
||||
self.human_name, self.description, self.misc, self.index
|
||||
"Name: {}, Description: {}, Extra: {}, Stable Index: {:?}",
|
||||
self.human_name, self.description, self.misc, self.stable_id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// fn step_chk(val: i64, default: i64, step: i64) -> Result<(), NokhwaError> {
|
||||
// if (val - default) % step != 0 {
|
||||
// return Err(NokhwaError::StructureError {
|
||||
// structure: "Value".to_string(),
|
||||
// error: "Doesnt fit step".to_string(),
|
||||
// });
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Backends {
|
||||
Video4Linux2,
|
||||
WebWASM,
|
||||
AVFoundation,
|
||||
MicrosoftMediaFoundation,
|
||||
OpenCV,
|
||||
Custom(&'static str),
|
||||
}
|
||||
|
||||
// /// The list of known capture backends to the library. <br>
|
||||
// /// - `Auto` - Use automatic selection.
|
||||
// /// - `AVFoundation` - Uses `AVFoundation` on `MacOSX`
|
||||
// /// - `Video4Linux` - `Video4Linux2`, a linux specific backend.
|
||||
// /// - `UniversalVideoClass` - ***DEPRECATED*** Universal Video Class (please check [libuvc](https://github.com/libuvc/libuvc)). Platform agnostic, although on linux it needs `sudo` permissions or similar to use.
|
||||
// /// - `MediaFoundation` - Microsoft Media Foundation, Windows only,
|
||||
// /// - `OpenCv` - Uses `OpenCV` to capture. Platform agnostic.
|
||||
// /// - `GStreamer` - ***DEPRECATED*** Uses `GStreamer` RTP to capture. Platform agnostic.
|
||||
// /// - `Browser` - Uses browser APIs to capture from a webcam.
|
||||
// pub enum SelectableBackend {
|
||||
// Auto,
|
||||
// Custom(&'static str),
|
||||
// AVFoundation,
|
||||
// Video4Linux,
|
||||
// UniversalVideoClass,
|
||||
// MediaFoundation,
|
||||
// OpenCv,
|
||||
// GStreamer,
|
||||
// Browser,
|
||||
// }
|
||||
//
|
||||
// /// The list of known capture backends to the library. <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:?}")
|
||||
// }
|
||||
// }
|
||||
impl Display for Backends {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub struct QueriedCamera {
|
||||
pub index: CameraIndex,
|
||||
pub information: CameraInformation
|
||||
}
|
||||
|
||||
@@ -5,10 +5,9 @@ edition = "2024"
|
||||
|
||||
[features]
|
||||
ffmpeg = ["ffmpeg-the-third"]
|
||||
av1 = ["rav1e"]
|
||||
yuyv = ["dcv-color-primitives", "yuv"]
|
||||
mjpeg = ["zune-jpeg", "zune-core"]
|
||||
static = ["ffmpeg-the-third/static"]
|
||||
#static = ["ffmpeg-the-third/static"]
|
||||
async = []
|
||||
|
||||
[dependencies]
|
||||
@@ -22,10 +21,6 @@ path = "../nokhwa-core"
|
||||
version = "3.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.rav1e]
|
||||
version = "0.8"
|
||||
optional = true
|
||||
|
||||
[dependencies.yuv]
|
||||
version = "0.8"
|
||||
optional = true
|
||||
@@ -41,3 +36,7 @@ optional = true
|
||||
[dependencies.zune-core]
|
||||
version = "0.5.0-rc2"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.image]
|
||||
workspace = true
|
||||
features = ["png", "jpeg", "avif"]
|
||||
|
||||
@@ -4,11 +4,10 @@ use ffmpeg_the_third::codec::{Context, Id, Parameters};
|
||||
use ffmpeg_the_third::decoder::Video;
|
||||
use ffmpeg_the_third::ffi::{
|
||||
AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace,
|
||||
AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket,
|
||||
AVPixelFormat, AVRational, SwsContext, av_frame_alloc, av_frame_move_ref,
|
||||
av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context,
|
||||
avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext,
|
||||
sws_scale_frame,
|
||||
AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, AVPixelFormat, AVRational,
|
||||
SwsContext, av_frame_alloc, av_frame_move_ref, av_image_copy_to_buffer, av_image_fill_arrays,
|
||||
av_image_get_buffer_size, avcodec_free_context, avcodec_parameters_alloc,
|
||||
avcodec_parameters_free, sws_freeContext, sws_getContext, sws_scale_frame,
|
||||
};
|
||||
use ffmpeg_the_third::packet::{Borrow, Ref};
|
||||
use ffmpeg_the_third::{Frame, decoder, packet::Packet};
|
||||
@@ -72,7 +71,7 @@ impl Decoder for FfmpegDecoder {
|
||||
&mut self,
|
||||
to_decode: FrameBuffer,
|
||||
mut buffer: impl AsMut<[u8]>,
|
||||
_destination_format: Option<Self::DestinationFormatHint>
|
||||
_destination_format: Option<Self::DestinationFormatHint>,
|
||||
) -> Result<Self::OutputMeta, NokhwaError> {
|
||||
// TODO: add an extra zippy happy path for rgb/bgr/luma
|
||||
let (frame, metadata) = self.receive_decoded_frame(to_decode)?;
|
||||
@@ -108,19 +107,20 @@ impl Decoder for FfmpegDecoder {
|
||||
where
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
{
|
||||
let destination_format =
|
||||
pixel_to_destination_px_fmt::<P>()
|
||||
.ok_or(NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()))?;
|
||||
let destination_format = pixel_to_destination_px_fmt::<P>().ok_or(
|
||||
NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()),
|
||||
)?;
|
||||
|
||||
let buffer = buffer.as_mut();
|
||||
let estimated_size =
|
||||
self.codec
|
||||
.preferred_buffer_min_size(&None)?
|
||||
.ok_or(NokhwaError::DecoderInvalidBuffer(
|
||||
let estimated_size = self.codec.preferred_buffer_min_size(&None)?.ok_or(
|
||||
NokhwaError::DecoderInvalidBuffer(
|
||||
"failed to estimate decoder buffer.length".to_string(),
|
||||
))?;
|
||||
),
|
||||
)?;
|
||||
if buffer.len() < estimated_size {
|
||||
return Err(NokhwaError::DecoderInvalidBuffer("buffer too small!".to_string()));
|
||||
return Err(NokhwaError::DecoderInvalidBuffer(
|
||||
"buffer too small!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let (mut frame, decoded_meta) = self.receive_decoded_frame(to_decode)?;
|
||||
@@ -198,7 +198,12 @@ impl Decoder for FfmpegDecoder {
|
||||
{
|
||||
let min_size = self.output_decoder_min_size_pixel::<P>(self.config().resolution);
|
||||
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(
|
||||
ImageBuffer::from_vec(
|
||||
self.codec.config.resolution.width(),
|
||||
@@ -212,9 +217,19 @@ impl Decoder for FfmpegDecoder {
|
||||
))
|
||||
}
|
||||
|
||||
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
|
||||
let size =
|
||||
unsafe { av_image_get_buffer_size(destination_format, resolution.width() as i32, resolution.height() as i32, 1) };
|
||||
fn output_decoder_min_size(
|
||||
&self,
|
||||
resolution: Resolution,
|
||||
destination_format: Self::DestinationFormatHint,
|
||||
) -> usize {
|
||||
let size = unsafe {
|
||||
av_image_get_buffer_size(
|
||||
destination_format,
|
||||
resolution.width() as i32,
|
||||
resolution.height() as i32,
|
||||
1,
|
||||
)
|
||||
};
|
||||
size as usize
|
||||
}
|
||||
|
||||
@@ -353,11 +368,13 @@ pub struct FfmpegCodec {
|
||||
|
||||
impl FfmpegCodec {
|
||||
fn new(config: <FfmpegCodec as Codec>::Config) -> Result<Self, NokhwaError> {
|
||||
let id = convert_format_to_codec_id(&config.frame_format)
|
||||
.ok_or(NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format))?;
|
||||
let id = convert_format_to_codec_id(&config.frame_format).ok_or(
|
||||
NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format),
|
||||
)?;
|
||||
|
||||
let codec =
|
||||
decoder::find(id).ok_or(NokhwaError::DecoderInitializationError("Failed to find codec".to_string()))?;
|
||||
let codec = decoder::find(id).ok_or(NokhwaError::DecoderInitializationError(
|
||||
"Failed to find codec".to_string(),
|
||||
))?;
|
||||
|
||||
let context = unsafe {
|
||||
let ptr = ffmpeg_the_third::ffi::avcodec_alloc_context3(codec.as_ptr());
|
||||
@@ -375,9 +392,11 @@ impl FfmpegCodec {
|
||||
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
|
||||
video
|
||||
.set_parameters(unsafe {
|
||||
Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::DecoderInitializationError(
|
||||
Parameters::from_raw(config.as_ptr()?).ok_or(
|
||||
NokhwaError::DecoderInitializationError(
|
||||
"Failed to convert parameters".to_string(),
|
||||
))?
|
||||
),
|
||||
)?
|
||||
})
|
||||
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
|
||||
|
||||
@@ -416,9 +435,11 @@ impl Codec for FfmpegCodec {
|
||||
let mut temp_config = config.as_avcodec_params()?;
|
||||
self.decoder
|
||||
.set_parameters(unsafe {
|
||||
Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::DecoderInvalidConfiguration(
|
||||
Parameters::from_raw(&mut temp_config).ok_or(
|
||||
NokhwaError::DecoderInvalidConfiguration(
|
||||
"Failed to convert parameters".to_string(),
|
||||
))?
|
||||
),
|
||||
)?
|
||||
})
|
||||
.map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?;
|
||||
self.config = config;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
extern crate core;
|
||||
|
||||
#[cfg(feature = "ffmpeg")]
|
||||
pub mod ffmpeg;
|
||||
#[cfg(feature = "mjpeg")]
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use zune_core::bytestream::ZCursor;
|
||||
use zune_core::colorspace::ColorSpace;
|
||||
pub use zune_core::options::DecoderOptions;
|
||||
use zune_jpeg::errors::DecodeErrors;
|
||||
pub use zune_jpeg::ImageInfo;
|
||||
use zune_jpeg::JpegDecoder;
|
||||
use nokhwa_core::codec::Codec;
|
||||
use bytemuck::cast_slice_mut;
|
||||
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel};
|
||||
use nokhwa_core::error::NokhwaError;
|
||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
||||
use nokhwa_core::image::Primitive;
|
||||
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
|
||||
use nokhwa_core::types::Resolution;
|
||||
use zune_core::bytestream::ZCursor;
|
||||
use zune_core::colorspace::ColorSpace;
|
||||
pub use zune_core::options::DecoderOptions;
|
||||
pub use zune_jpeg::ImageInfo;
|
||||
use zune_jpeg::JpegDecoder;
|
||||
use zune_jpeg::errors::DecodeErrors;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MJpegDecoder {
|
||||
@@ -30,7 +31,12 @@ impl Decoder for MJpegDecoder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option<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 cursor = ZCursor::new(to_decode.as_ref());
|
||||
|
||||
@@ -38,7 +44,10 @@ impl Decoder for MJpegDecoder {
|
||||
|
||||
let mut config = self.config.decoder_options;
|
||||
if let Some(dest_hint) = destination_format_hint {
|
||||
config = self.config.decoder_options.jpeg_set_out_colorspace(dest_hint.into());
|
||||
config = self
|
||||
.config
|
||||
.decoder_options
|
||||
.jpeg_set_out_colorspace(dest_hint.into());
|
||||
}
|
||||
|
||||
decoder.set_options(config);
|
||||
@@ -47,39 +56,64 @@ impl Decoder for MJpegDecoder {
|
||||
let info = match decoder.info() {
|
||||
Some(i) => i,
|
||||
|
||||
None => return Err(NokhwaError::Decoder("??????? how did we get here".to_string()))
|
||||
None => {
|
||||
return Err(NokhwaError::Decoder(
|
||||
"??????? how did we get here".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(info.into())
|
||||
}
|
||||
|
||||
fn decode_to_pixel_buffer<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
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
{
|
||||
let hint = match pixel_to_colorspace::<P>() {
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
{
|
||||
let min_size = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
|
||||
let mut buffer = vec![0_u8; min_size];
|
||||
let output_metadata = self.decode_to_pixel_buffer(to_decode, buffer.as_mut_slice())?;
|
||||
let image_buffer = ImageBuffer::from_raw(output_metadata.resolution.width(), output_metadata.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to make imagebuffer".to_string()))?;
|
||||
Ok(DecodedImage::new(
|
||||
image_buffer,
|
||||
output_metadata
|
||||
))
|
||||
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MAX_VALUE; min_size];
|
||||
let output_metadata =
|
||||
self.decode_to_pixel_buffer::<P>(to_decode, out_buffer.as_mut_slice())?;
|
||||
let image_buffer = ImageBuffer::from_raw(
|
||||
output_metadata.resolution.width(),
|
||||
output_metadata.resolution.height(),
|
||||
out_buffer,
|
||||
)
|
||||
.ok_or(NokhwaError::Decoder(
|
||||
"Failed to make imagebuffer".to_string(),
|
||||
))?;
|
||||
Ok(DecodedImage::new(image_buffer, output_metadata))
|
||||
}
|
||||
|
||||
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
|
||||
fn output_decoder_min_size(
|
||||
&self,
|
||||
resolution: Resolution,
|
||||
destination_format: Self::DestinationFormatHint,
|
||||
) -> usize {
|
||||
let stride = match destination_format {
|
||||
OutputColor::Rgb => 3,
|
||||
OutputColor::RgbA => 4,
|
||||
@@ -122,7 +156,7 @@ impl From<ImageInfo> for ImageMeta {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MJpegOptions {
|
||||
pub resolution: Resolution,
|
||||
pub decoder_options: DecoderOptions
|
||||
pub decoder_options: DecoderOptions,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
@@ -135,9 +169,9 @@ pub enum OutputColor {
|
||||
LumaA,
|
||||
}
|
||||
|
||||
impl Into<ColorSpace> for OutputColor {
|
||||
fn into(self) -> ColorSpace {
|
||||
match self {
|
||||
impl From<OutputColor> for ColorSpace {
|
||||
fn from(val: OutputColor) -> Self {
|
||||
match val {
|
||||
OutputColor::Rgb => ColorSpace::RGB,
|
||||
OutputColor::RgbA => ColorSpace::RGBA,
|
||||
OutputColor::Bgr => ColorSpace::BGR,
|
||||
@@ -148,7 +182,11 @@ impl Into<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 {
|
||||
"RGBA" => Some(OutputColor::RgbA),
|
||||
"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 {
|
||||
match decode_errors {
|
||||
DecodeErrors::Format(fmt) => {
|
||||
NokhwaError::Decoder(fmt)
|
||||
}
|
||||
DecodeErrors::FormatStatic(fmt) => {
|
||||
NokhwaError::Decoder(fmt.to_string())
|
||||
}
|
||||
DecodeErrors::Format(fmt) => NokhwaError::Decoder(fmt),
|
||||
DecodeErrors::FormatStatic(fmt) => NokhwaError::Decoder(fmt.to_string()),
|
||||
DecodeErrors::IllegalMagicBytes(b) => {
|
||||
NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}"))
|
||||
}
|
||||
@@ -177,9 +211,10 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
||||
DecodeErrors::ZeroError => {
|
||||
NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string())
|
||||
}
|
||||
DecodeErrors::DqtError(e) | DecodeErrors::MCUError(e) | DecodeErrors::SosError(e) | DecodeErrors::SofError(e)=> {
|
||||
NokhwaError::Decoder(format!("error decoding: {e}"))
|
||||
}
|
||||
DecodeErrors::DqtError(e)
|
||||
| DecodeErrors::MCUError(e)
|
||||
| DecodeErrors::SosError(e)
|
||||
| DecodeErrors::SofError(e) => NokhwaError::Decoder(format!("error decoding: {e}")),
|
||||
DecodeErrors::Unsupported(why) => {
|
||||
NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}"))
|
||||
}
|
||||
@@ -192,8 +227,6 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
|
||||
DecodeErrors::TooSmallOutput(a, b) => {
|
||||
NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}"))
|
||||
}
|
||||
DecodeErrors::IoErrors(io) => {
|
||||
NokhwaError::Decoder(format!("io error: {io:?}"))
|
||||
}
|
||||
DecodeErrors::IoErrors(io) => NokhwaError::Decoder(format!("io error: {io:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
+463
-156
@@ -1,15 +1,35 @@
|
||||
use bytemuck::{cast_slice, cast_slice_mut, try_cast_slice_mut};
|
||||
|
||||
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive};
|
||||
use nokhwa_core::error::NokhwaError;
|
||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
||||
use nokhwa_core::frame_format::FrameFormat;
|
||||
use nokhwa_core::types::{CameraFormat, Resolution};
|
||||
use yuv::{ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb, p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr, uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16, vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba, vyuy422_to_rgba_p16, yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba, yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr, yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra, yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb, yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra, yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange, YuvStandardMatrix};
|
||||
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive};
|
||||
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
|
||||
use nokhwa_core::types::{CameraFormat, Resolution};
|
||||
use yuv::{
|
||||
YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange,
|
||||
YuvStandardMatrix, ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb,
|
||||
p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr,
|
||||
uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16,
|
||||
vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba,
|
||||
vyuy422_to_rgba_p16, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba,
|
||||
yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr,
|
||||
yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra,
|
||||
yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb,
|
||||
yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba,
|
||||
yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra,
|
||||
yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr,
|
||||
yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16,
|
||||
};
|
||||
|
||||
pub struct YUVDecoder {
|
||||
config: YUVConfig,
|
||||
stride_cache: Option<CachedStride>,
|
||||
}
|
||||
|
||||
impl YUVDecoder {
|
||||
pub fn new(config: <Self as Decoder>::Config) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for YUVDecoder {
|
||||
@@ -23,85 +43,88 @@ impl Decoder for YUVDecoder {
|
||||
|
||||
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
|
||||
if !FrameFormat::YCBCR.contains(&config.yuv_type) {
|
||||
return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type))
|
||||
return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type));
|
||||
}
|
||||
self.config = config;
|
||||
self.stride_cache = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_to_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, destination_format: Option<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 {
|
||||
Some(df) => df,
|
||||
None => return Err(NokhwaError::DecoderDestinationHintRequired)
|
||||
None => return Err(NokhwaError::DecoderDestinationHintRequired),
|
||||
};
|
||||
|
||||
let buffer = buffer.as_mut();
|
||||
if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format) {
|
||||
return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string()))
|
||||
return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string()));
|
||||
}
|
||||
|
||||
let stride = match self.stride_cache {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
self.stride_cache = figure_out_stride(self.config.yuv_type);
|
||||
match self.stride_cache {
|
||||
Some(s) => s,
|
||||
None => return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)),
|
||||
}
|
||||
}
|
||||
};
|
||||
let stride = figure_out_stride(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?;
|
||||
let byte_width = figure_out_byte_width(self.config.yuv_type).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))?;
|
||||
|
||||
|
||||
let stride_3px = 3 *
|
||||
self.config.resolution.width();
|
||||
let stride_4px = 4 * self.config.resolution.width();
|
||||
let stride_3px_2w = 6 * self.config.resolution.width();
|
||||
let stride_4px_2w = 8 * self.config.resolution.width();
|
||||
|
||||
// todo: clean up ts into a macro </3
|
||||
let decode_status = match stride {
|
||||
CachedStride::Packed(stride) => {
|
||||
let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride);
|
||||
Stride::Packed(stride) => {
|
||||
let image = prepare_to_packed_image(&to_decode, self.config.resolution, byte_width, stride);
|
||||
match self.config.yuv_type {
|
||||
FrameFormat::Ayuv_32 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
|
||||
YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
|
||||
YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
|
||||
YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::Yuyv_4_2_2 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, 8, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, stride_4px_2w, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
}
|
||||
}
|
||||
FrameFormat::Uyvy_4_2_2 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
}
|
||||
}
|
||||
FrameFormat::Vyuy_4_2_2 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
}
|
||||
}
|
||||
FrameFormat::Yvyu_4_2_2 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -113,78 +136,79 @@ impl Decoder for YUVDecoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
CachedStride::SemiPlanar(y_stride, uv_stride) => {
|
||||
let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, y_stride, uv_stride);
|
||||
Stride::Semi(y_stride, uv_stride) => {
|
||||
let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, uv_stride);
|
||||
match self.config.yuv_type {
|
||||
FrameFormat::NV24 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::NV42 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::NV16 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::NV61 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::NV12 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::NV21 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::P010 => {
|
||||
let a = convert_bi_planar_image_to_u16(image);
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(p010_to_rgb(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(p010_to_rgba(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(p010_to_bgr(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(p010_to_bgra(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb16 => Some(p010_to_rgb10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(p010_to_rgba10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
|
||||
YUVDestination::Rgb16 => Some(p010_to_rgb10(&a, cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(p010_to_rgba10(&a, cast_slice_mut(buffer), stride_4px, self.config.range, self.config.matrix)),
|
||||
// _ => None,
|
||||
}
|
||||
}
|
||||
FrameFormat::P012 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_4px_2w, self.config.range, self.config.matrix)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -197,15 +221,15 @@ impl Decoder for YUVDecoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
CachedStride::Planar(y_stride, u_stride, v_stride) => {
|
||||
let image = prepare_to_planar_image(&to_decode, self.config.resolution, y_stride, u_stride, v_stride);
|
||||
Stride::Planar(y_stride, u_stride, v_stride, line_ratio) => {
|
||||
let image = prepare_to_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, u_stride, v_stride, line_ratio);
|
||||
match self.config.yuv_type {
|
||||
FrameFormat::Yuv_4_2_0 => {
|
||||
match destination_format {
|
||||
YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
|
||||
YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -222,16 +246,27 @@ impl Decoder for YUVDecoder {
|
||||
match decode_status {
|
||||
Some(Ok(_)) => Ok(()),
|
||||
Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())),
|
||||
None => Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)),
|
||||
None => Err(NokhwaError::DecoderUnsupportedFrameFormat(
|
||||
self.config.yuv_type,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_to_pixel_buffer<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
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth
|
||||
<P as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
{
|
||||
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,
|
||||
};
|
||||
let buffer = buffer.as_mut();
|
||||
@@ -242,23 +277,34 @@ impl Decoder for YUVDecoder {
|
||||
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
|
||||
<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 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)?;
|
||||
Ok(
|
||||
DecodedImage::new(
|
||||
ImageBuffer::from_vec(self.config.resolution.width(), self.config.resolution.height(), out_buffer)
|
||||
.ok_or(NokhwaError::Decoder("failed to convert into an image buffer".to_string()))?,
|
||||
()
|
||||
)
|
||||
Ok(DecodedImage::new(
|
||||
ImageBuffer::from_vec(
|
||||
self.config.resolution.width(),
|
||||
self.config.resolution.height(),
|
||||
out_buffer,
|
||||
)
|
||||
.ok_or(NokhwaError::Decoder(
|
||||
"failed to convert into an image buffer".to_string(),
|
||||
))?,
|
||||
(),
|
||||
))
|
||||
}
|
||||
|
||||
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
|
||||
fn output_decoder_min_size(
|
||||
&self,
|
||||
resolution: Resolution,
|
||||
destination_format: Self::DestinationFormatHint,
|
||||
) -> usize {
|
||||
let px_size = match destination_format {
|
||||
YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3,
|
||||
YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4,
|
||||
@@ -285,30 +331,32 @@ pub enum 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 {
|
||||
"RGB" => {
|
||||
match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||
"RGB" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||
1 => Some(YUVDestination::Rgb8),
|
||||
2 => Some(YUVDestination::Rgb16),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
"RGBA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||
1 => Some(YUVDestination::Rgba8),
|
||||
2 => Some(YUVDestination::Rgba16),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
"BGR" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||
1 => Some(YUVDestination::Bgr8),
|
||||
// 2 => Some(YUVDestination::Bgr16),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
"BGRA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
|
||||
1 => Some(YUVDestination::Bgra8),
|
||||
// 2 => Some(YUVDestination::Bgra16),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -329,7 +377,7 @@ impl TryFrom<CameraFormat> for YUVConfig {
|
||||
|
||||
fn try_from(value: CameraFormat) -> Result<Self, Self::Error> {
|
||||
if !FrameFormat::YCBCR.contains(&value.format()) {
|
||||
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()))
|
||||
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()));
|
||||
}
|
||||
Ok(YUVConfig {
|
||||
resolution: value.resolution(),
|
||||
@@ -342,33 +390,69 @@ impl TryFrom<CameraFormat> for YUVConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
enum CachedStride {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Stride {
|
||||
Packed(u32),
|
||||
SemiPlanar(u32, u32),
|
||||
Planar(u32, u32, u32),
|
||||
Semi(u32, u32),
|
||||
Planar(u32, u32, u32, u32),
|
||||
}
|
||||
|
||||
fn figure_out_stride(frame_format: FrameFormat) -> Option<CachedStride> {
|
||||
if let Some(s) = packed_stride_component_per_4px(frame_format) {
|
||||
return Some(CachedStride::Packed(s));
|
||||
fn figure_out_stride(frame_format: FrameFormat) -> Option<Stride> {
|
||||
if let Some(yuy) = packed_stride_component(frame_format) {
|
||||
return Some(Stride::Packed(yuy));
|
||||
}
|
||||
if let Some((s1, s2)) = semiplanar_stride_per_4px(frame_format) {
|
||||
return Some(CachedStride::SemiPlanar(s1, s2));
|
||||
if let Some((y, uv)) = semiplanar_stride(frame_format) {
|
||||
return Some(Stride::Semi(y, uv));
|
||||
}
|
||||
if let Some((s1, s2, s3)) = planar_stride_per_4px(frame_format) {
|
||||
return Some(CachedStride::Planar(s1, s2, s3));
|
||||
// l here means the ratio of luma lines to chroma lines
|
||||
// u_r and v_r are defined as _ratios_ to the luma stride, i.e. how many luma components
|
||||
// per chroma component, u_r = 2 means 2 luma per 1 u chroma
|
||||
if let Some((y, u_r, v_r, l)) = planar_stride(frame_format) {
|
||||
return Some(Stride::Planar(y, u_r, v_r, l));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn packed_stride_component_per_4px(format: FrameFormat) -> Option<u32> {
|
||||
fn figure_out_byte_width(format: FrameFormat) -> Option<u32> {
|
||||
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::Uyvy_4_2_2
|
||||
| FrameFormat::Vyuy_4_2_2
|
||||
| FrameFormat::Yvyu_4_2_2 => Some(16),
|
||||
| FrameFormat::Yvyu_4_2_2 => Some(2),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn semiplanar_stride(format: FrameFormat) -> Option<(u32, u32)> {
|
||||
match format {
|
||||
FrameFormat::NV24 | FrameFormat::NV42 => Some((1, 2)),
|
||||
FrameFormat::NV16 | FrameFormat::NV61 => Some((1, 1)),
|
||||
FrameFormat::P010 | FrameFormat::P012 => Some((1, 1)),
|
||||
FrameFormat::NV12 | FrameFormat::NV21 => Some((1, 1)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn planar_stride(format: FrameFormat) -> Option<(u32, u32, u32, u32)> {
|
||||
match format {
|
||||
// if you are here wondering if I will ever add another planar format
|
||||
// the answer is no.
|
||||
// do not bother opening an issue or a pr i will never merge it use a sane format like nv12
|
||||
FrameFormat::Yuv_4_2_0 => Some((1, 2, 2, 2)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -376,76 +460,78 @@ fn packed_stride_component_per_4px(format: FrameFormat) -> Option<u32> {
|
||||
fn prepare_to_packed_image<'a>(
|
||||
frame_buffer: &'a FrameBuffer<'a>,
|
||||
resolution: Resolution,
|
||||
byte_width: u32,
|
||||
yuy_stride: u32,
|
||||
) -> YuvPackedImage<'a, u8> {
|
||||
YuvPackedImage {
|
||||
yuy: frame_buffer.buffer(),
|
||||
yuy_stride,
|
||||
yuy_stride: yuy_stride * resolution.width(),
|
||||
width: resolution.width(),
|
||||
height: resolution.height(),
|
||||
}
|
||||
}
|
||||
|
||||
fn semiplanar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32)> {
|
||||
match format {
|
||||
FrameFormat::NV24 | FrameFormat::NV42 => Some((4, 8)),
|
||||
FrameFormat::NV16 | FrameFormat::NV61 => Some((4, 4)),
|
||||
FrameFormat::NV12 | FrameFormat::NV21 | FrameFormat::P010 | FrameFormat::P012 => {
|
||||
Some((4, 4))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_to_semi_planar_image<'a>(
|
||||
frame_buffer: &'a FrameBuffer<'a>,
|
||||
resolution: Resolution,
|
||||
byte_width: u32,
|
||||
y_stride: u32,
|
||||
uv_stride: u32,
|
||||
) -> YuvBiPlanarImage<'a, u8> {
|
||||
let uv_start = (resolution.width() * resolution.height()) as usize;
|
||||
let uv_start = (resolution.width() * resolution.height() * byte_width) as usize;
|
||||
YuvBiPlanarImage {
|
||||
y_plane: &frame_buffer.buffer()[0..uv_start],
|
||||
y_stride,
|
||||
y_stride: y_stride * resolution.width(),
|
||||
uv_plane: &frame_buffer.buffer()[uv_start..frame_buffer.len()],
|
||||
uv_stride,
|
||||
uv_stride: uv_stride * resolution.width(),
|
||||
width: resolution.width(),
|
||||
height: resolution.height(),
|
||||
}
|
||||
}
|
||||
|
||||
fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> {
|
||||
match format {
|
||||
FrameFormat::Yuv_4_2_0 => Some((4, 1, 1)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> {
|
||||
// match format {
|
||||
// FrameFormat::Yuv_4_2_0 => Some((stride_ 1, 1)),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
|
||||
// does this work? idk
|
||||
fn prepare_to_planar_image<'a>(
|
||||
frame_buffer: &'a FrameBuffer<'a>,
|
||||
resolution: Resolution,
|
||||
y_stride: u32,
|
||||
u_stride: u32,
|
||||
v_stride: u32,
|
||||
byte_width: u32,
|
||||
y_stride_base: u32,
|
||||
u_stride_ratio: u32,
|
||||
v_stride_ratio: u32,
|
||||
line_ratio: u32,
|
||||
) -> YuvPlanarImage<'a, u8> {
|
||||
let u_start = (resolution.width() * resolution.height()) as usize;
|
||||
let v_start =
|
||||
u_start + ((resolution.width() / 4) * y_stride * (resolution.height() * u_stride)) as usize;
|
||||
|
||||
let y_stride = resolution.width() * y_stride_base;
|
||||
let u_stride = y_stride / u_stride_ratio;
|
||||
let v_stride = y_stride / v_stride_ratio;
|
||||
let chroma_lines = resolution.height() / line_ratio;
|
||||
|
||||
// size of y area
|
||||
let u_start = resolution.height() * resolution.width() * byte_width;
|
||||
let v_start = u_start + u_stride * chroma_lines * byte_width;
|
||||
|
||||
let us = u_start as usize;
|
||||
let vs = v_start as usize;
|
||||
|
||||
YuvPlanarImage {
|
||||
y_plane: &frame_buffer.buffer()[0..u_start],
|
||||
y_plane: &frame_buffer.buffer()[0..us],
|
||||
y_stride,
|
||||
u_plane: &frame_buffer.buffer()[u_start..v_start],
|
||||
u_plane: &frame_buffer.buffer()[us..vs],
|
||||
u_stride,
|
||||
v_plane: &frame_buffer.buffer()[v_start..frame_buffer.len()],
|
||||
v_plane: &frame_buffer.buffer()[vs..frame_buffer.len()],
|
||||
v_stride,
|
||||
width: resolution.width(),
|
||||
height: resolution.height(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_packed_image_to_u16(
|
||||
yuv_packed_image: YuvPackedImage<u8>,
|
||||
) -> YuvPackedImage<u16> {
|
||||
fn convert_packed_image_to_u16(yuv_packed_image: YuvPackedImage<u8>) -> YuvPackedImage<u16> {
|
||||
let buf = cast_slice(yuv_packed_image.yuy);
|
||||
YuvPackedImage {
|
||||
yuy: buf,
|
||||
@@ -469,3 +555,224 @@ fn convert_bi_planar_image_to_u16(
|
||||
height: yuv_bi_planar_image.height,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::yuv::{YUVConfig, YUVDecoder};
|
||||
use image::{DynamicImage, EncodableLayout, ImageBuffer, ImageFormat, ImageReader, Pixel, PixelWithColorType, Rgb, Rgba};
|
||||
use nokhwa_core::decoder::Decoder;
|
||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
||||
use nokhwa_core::frame_format::FrameFormat;
|
||||
use nokhwa_core::image::NonFloatScalarWidth;
|
||||
use nokhwa_core::types::Resolution;
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter, Read};
|
||||
use yuv::{YuvConversionMode, YuvRange, YuvStandardMatrix};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum PixelFormat {
|
||||
Rgb8,
|
||||
RgbA8,
|
||||
Rgb16,
|
||||
RgbA16,
|
||||
}
|
||||
|
||||
fn base_test<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