mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
Add fractional suppprt with new FrameRate type, merge SourceFrameFormat and FrameFormat
This commit is contained in:
Generated
+3984
File diff suppressed because it is too large
Load Diff
+11
-3
@@ -19,9 +19,10 @@ exclude = ["examples/jscam"]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["decoding"]
|
||||
default = ["decoding-yuv","decoding-mozjpeg"]
|
||||
serialize = ["serde", "nokhwa-core/serialize"]
|
||||
decoding = ["nokhwa-core/mjpeg"]
|
||||
decoding-yuv = ["mozjpeg"]
|
||||
decoding-mozjpeg = ["mozjpeg"]
|
||||
input-avfoundation = ["nokhwa-bindings-macos", "flume"]
|
||||
input-msmf = ["nokhwa-bindings-windows"]
|
||||
input-v4l = ["nokhwa-bindings-linux"]
|
||||
@@ -43,7 +44,14 @@ test-fail-warning = []
|
||||
[dependencies]
|
||||
thiserror = "1.0"
|
||||
paste = "1.0"
|
||||
dcv-color-primitives = "0.5"
|
||||
|
||||
[dependencies.mozjpeg]
|
||||
version = "0.9"
|
||||
optional = true
|
||||
|
||||
[dependencies.dcv-color-primitives]
|
||||
version = "0.5"
|
||||
optional = true
|
||||
|
||||
[dependencies.nokhwa-core]
|
||||
version = "0.2"
|
||||
|
||||
@@ -14,9 +14,8 @@ repository = "https://github.com/l1npengtul/nokhwa"
|
||||
default = []
|
||||
serialize = ["serde"]
|
||||
wgpu-types = ["wgpu"]
|
||||
mjpeg = ["mozjpeg"]
|
||||
opencv-mat = ["opencv"]
|
||||
docs-features = ["serialize", "wgpu-types", "mjpeg"]
|
||||
docs-features = ["serialize", "wgpu-types"]
|
||||
async = ["async-trait"]
|
||||
test-fail-warnings = []
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ impl Buffer {
|
||||
Mat_AUTO_STEP,
|
||||
)
|
||||
.map_err(|why| NokhwaError::ProcessFrameError {
|
||||
src: FrameFormat::RAWRGB,
|
||||
src: FrameFormat::Rgb8,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: why.to_string(),
|
||||
})?;
|
||||
@@ -156,6 +156,7 @@ impl Buffer {
|
||||
|
||||
#[cfg(feature = "wgpu-types")]
|
||||
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, ImageCopyTexture, TextureAspect, ImageDataLayout};
|
||||
use crate::frame_format::FrameFormat;
|
||||
|
||||
#[cfg(feature = "wgpu-types")]
|
||||
impl Buffer {
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::ops::Deref;
|
||||
use image::{ImageBuffer, Pixel};
|
||||
use serde::de::Error;
|
||||
use crate::buffer::Buffer;
|
||||
use crate::frame_format::{SourceFrameFormat};
|
||||
use crate::frame_format::FrameFormat;
|
||||
|
||||
/// Trait to define a struct that can decode a [`Buffer`]
|
||||
pub trait Decoder {
|
||||
/// Formats that the decoder can decode.
|
||||
const ALLOWED_FORMATS: &'static [SourceFrameFormat];
|
||||
const ALLOWED_FORMATS: &'static FrameFormat;
|
||||
/// Output pixel type (e.g. [`Rgb<u8>`](image::Rgb))
|
||||
type Pixel: Pixel;
|
||||
/// Container for [`Self::Pixel`] - must have the same [`Pixel::Subpixel`]
|
||||
|
||||
@@ -59,6 +59,7 @@ pub enum FrameFormat {
|
||||
|
||||
// Custom
|
||||
Custom(u128),
|
||||
PlatformSpecificCustomFormat(PlatformFrameFormat),
|
||||
}
|
||||
|
||||
impl FrameFormat {
|
||||
@@ -165,82 +166,3 @@ impl Display for PlatformFrameFormat {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
/// The Source Format of a [`Buffer`].
|
||||
///
|
||||
/// May either be a platform specific FourCC, or a FrameFormat
|
||||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum SourceFrameFormat {
|
||||
FrameFormat(FrameFormat),
|
||||
PlatformSpecific(PlatformFrameFormat),
|
||||
}
|
||||
|
||||
impl From<FrameFormat> for SourceFrameFormat {
|
||||
fn from(value: FrameFormat) -> Self {
|
||||
SourceFrameFormat::FrameFormat(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(ApiBackend, u128)> for SourceFrameFormat {
|
||||
fn from(value: (ApiBackend, u128)) -> Self {
|
||||
SourceFrameFormat::PlatformSpecific(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlatformFrameFormat> for SourceFrameFormat {
|
||||
fn from(value: PlatformFrameFormat) -> Self {
|
||||
SourceFrameFormat::PlatformSpecific(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<FrameFormat> for SourceFrameFormat {
|
||||
fn eq(&self, other: &FrameFormat) -> bool {
|
||||
if let SourceFrameFormat::FrameFormat(ff) = self {
|
||||
ff == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<(ApiBackend, u128)> for SourceFrameFormat {
|
||||
fn eq(&self, other: &(ApiBackend, u128)) -> bool {
|
||||
if let SourceFrameFormat::PlatformSpecific(pff) = self {
|
||||
pff == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PartialEq<PlatformFrameFormat> for SourceFrameFormat {
|
||||
fn eq(&self, other: &PlatformFrameFormat) -> bool {
|
||||
if let SourceFrameFormat::PlatformSpecific(pff) = self {
|
||||
pff == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SourceFrameFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FormatDecoders<T: Pixel, E: Error>: Send + Sync {
|
||||
const NAME: &'static str;
|
||||
|
||||
const PRIMARY: &'static [FrameFormat];
|
||||
|
||||
const PLATFORM_ACCEPTABLE: &'static [(ApiBackend, &'static [u128])];
|
||||
|
||||
type Container: Deref<Target = [T::Subpixel]>;
|
||||
|
||||
fn decode(&self, buffer: &Buffer) -> Result<ImageBuffer<T, Self::Container>, E>;
|
||||
}
|
||||
|
||||
// TODO: Wgpu Decoder
|
||||
|
||||
// TODO: OpenCV Mat Decoder
|
||||
|
||||
+115
-11
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
error::NokhwaError,
|
||||
frame_format::{FrameFormat, SourceFrameFormat},
|
||||
frame_format::{FrameFormat},
|
||||
};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -304,20 +304,124 @@ impl Ord for Resolution {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
/// The frame rate of a camera.
|
||||
pub enum FrameRate {
|
||||
/// The driver reports the frame rate as a clean integer (e.g. 30 FPS).
|
||||
Integer(u32),
|
||||
/// The driver reports the frame rate as a floating point number (e.g. 29.97 FPS)
|
||||
Float(f32),
|
||||
/// The driver reports the frame rate as a fraction (e.g. 2997/1000 FPS)
|
||||
Fraction {
|
||||
numerator: u16,
|
||||
denominator: u16,
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameRate {
|
||||
pub fn new_integer(fps: u32) -> Self {
|
||||
FrameRate::Integer(fps)
|
||||
}
|
||||
|
||||
pub fn new_float(fps: f32) -> Self {
|
||||
FrameRate::Float(fps)
|
||||
}
|
||||
|
||||
pub fn new_fraction(numerator: u16, denominator: u16) -> Self {
|
||||
FrameRate::Fraction {
|
||||
numerator,
|
||||
denominator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> f32 {
|
||||
match self {
|
||||
FrameRate::Integer(fps) => fps as f32,
|
||||
FrameRate::Float(fps) => fps,
|
||||
FrameRate::Fraction { numerator, denominator } => (numerator as f32) / (denominator as f32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
match self {
|
||||
FrameRate::Integer(fps) => *fps,
|
||||
FrameRate::Float(fps) => fps as u32,
|
||||
FrameRate::Fraction { numerator, denominator } => numerator / denominator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FrameRate {
|
||||
fn default() -> Self {
|
||||
FrameRate::Integer(30)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for FrameRate {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
let this_float = self.as_float();
|
||||
let other = other.as_float();
|
||||
this_float.partial_cmp(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for FrameRate {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let this_float = self.as_float();
|
||||
let other = other.as_float();
|
||||
this_float.total_cmp(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FrameRate {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FrameRate::Integer(fps) => write!(f, "Framerate: {fps} FPS"),
|
||||
FrameRate::Float(fps) => write!(f, "Framerate: {fps} FPS"),
|
||||
FrameRate::Fraction { .. } => {
|
||||
let as_float = self.as_float();
|
||||
write!(f, "Framerate: {as_float} FPS")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for FrameRate {
|
||||
fn from(value: u32) -> Self {
|
||||
FrameRate::Integer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for FrameRate {
|
||||
fn from(value: f32) -> Self {
|
||||
FrameRate::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u16, u16)> for FrameRate {
|
||||
fn from(value: (u16, u16)) -> Self {
|
||||
FrameRate::Fraction {
|
||||
numerator: value.0,
|
||||
denominator: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a convenience struct that holds all information about the format of a webcam stream.
|
||||
/// It consists of a [`Resolution`], [`FrameFormat`], and a frame rate(u8).
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct CameraFormat {
|
||||
resolution: Resolution,
|
||||
format: SourceFrameFormat,
|
||||
frame_rate: u32,
|
||||
format: FrameFormat,
|
||||
frame_rate: FrameRate,
|
||||
}
|
||||
|
||||
impl CameraFormat {
|
||||
/// Construct a new [`CameraFormat`]
|
||||
#[must_use]
|
||||
pub fn new(resolution: Resolution, format: SourceFrameFormat, frame_rate: u32) -> Self {
|
||||
pub fn new(resolution: Resolution, format: FrameFormat, frame_rate: FrameRate) -> Self {
|
||||
CameraFormat {
|
||||
resolution,
|
||||
format,
|
||||
@@ -327,7 +431,7 @@ impl CameraFormat {
|
||||
|
||||
/// [`CameraFormat::new()`], but raw.
|
||||
#[must_use]
|
||||
pub fn new_from(res_x: u32, res_y: u32, format: SourceFrameFormat, fps: u32) -> Self {
|
||||
pub fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: FrameRate) -> Self {
|
||||
CameraFormat {
|
||||
resolution: Resolution {
|
||||
width_x: res_x,
|
||||
@@ -363,23 +467,23 @@ impl CameraFormat {
|
||||
|
||||
/// Get the frame rate of the current [`CameraFormat`]
|
||||
#[must_use]
|
||||
pub fn frame_rate(&self) -> u32 {
|
||||
pub fn frame_rate(&self) -> FrameRate {
|
||||
self.frame_rate
|
||||
}
|
||||
|
||||
/// Set the [`CameraFormat`]'s frame rate.
|
||||
pub fn set_frame_rate(&mut self, frame_rate: u32) {
|
||||
pub fn set_frame_rate(&mut self, frame_rate: FrameRate) {
|
||||
self.frame_rate = frame_rate;
|
||||
}
|
||||
|
||||
/// Get the [`CameraFormat`]'s format.
|
||||
#[must_use]
|
||||
pub fn format(&self) -> SourceFrameFormat {
|
||||
pub fn format(&self) -> FrameFormat {
|
||||
self.format
|
||||
}
|
||||
|
||||
/// Set the [`CameraFormat`]'s format.
|
||||
pub fn set_format(&mut self, format: SourceFrameFormat) {
|
||||
pub fn set_format(&mut self, format: FrameFormat) {
|
||||
self.format = format;
|
||||
}
|
||||
}
|
||||
@@ -388,8 +492,8 @@ impl Default for CameraFormat {
|
||||
fn default() -> Self {
|
||||
CameraFormat {
|
||||
resolution: Resolution::new(640, 480),
|
||||
format: SourceFrameFormat::FrameFormat(FrameFormat::MJpeg),
|
||||
frame_rate: 30,
|
||||
format: FrameFormat::MJpeg,
|
||||
frame_rate: FrameRate::Integer(30),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-3
@@ -4,6 +4,42 @@ use nokhwa_core::decoder::{Decoder, IdemptDecoder, StaticDecoder};
|
||||
use nokhwa_core::error::NokhwaError;
|
||||
use nokhwa_core::frame_format::{FrameFormat, SourceFrameFormat};
|
||||
|
||||
#[inline]
|
||||
fn decompress(
|
||||
data: &[u8],
|
||||
rgba: bool,
|
||||
) -> Result<, NokhwaError> {
|
||||
use mozjpeg::Decompress;
|
||||
|
||||
match Decompress::new_mem(data) {
|
||||
Ok(decompress) => {
|
||||
let decompressor_res = if rgba {
|
||||
decompress.rgba()
|
||||
} else {
|
||||
decompress.rgb()
|
||||
};
|
||||
match decompressor_res {
|
||||
Ok(decompressor) => Ok(decompressor),
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: FrameFormat::MJpeg,
|
||||
destination: "RGB888".to_string(),
|
||||
error: why.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: FrameFormat::MJpeg,
|
||||
destination: "RGB888".to_string(),
|
||||
error: why.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct MJPegDecoder;
|
||||
|
||||
impl Decoder for MJPegDecoder {
|
||||
@@ -15,16 +51,32 @@ impl Decoder for MJPegDecoder {
|
||||
fn decode(&mut self, buffer: Buffer) -> Result<ImageBuffer<Self::Pixel, Self::Container>, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn decode_buffer(&mut self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn predicted_size_of_frame(&mut self) -> Option<usize> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticDecoder for MJPegDecoder {
|
||||
fn decode_static(buffer: Buffer) -> Result<ImageBuffer<Self::Pixel, Self::Container>, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IdemptDecoder for MJPegDecoder {
|
||||
fn decode_nm(buffer: Buffer) -> Result<ImageBuffer<Self::Pixel, Self::Container>, Self::Error> {
|
||||
fn decode_static_to_buffer(buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IdemptDecoder for MJPegDecoder {
|
||||
fn decode_nm(&self, buffer: Buffer) -> Result<ImageBuffer<Self::Pixel, Self::Container>, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn decode_nm_to_buffer(&self, buffer: &mut [Pixel::Subpixel]) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user