Add fractional suppprt with new FrameRate type, merge SourceFrameFormat and FrameFormat

This commit is contained in:
l1npengtul
2023-10-04 01:19:23 +09:00
parent dbdb42bdc6
commit ae0d818e1c
8 changed files with 4171 additions and 101 deletions
Generated
+3984
View File
File diff suppressed because it is too large Load Diff
+11 -3
View File
@@ -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"
+1 -2
View File
@@ -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 = []
+2 -1
View File
@@ -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 -2
View File
@@ -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`]
+1 -79
View File
@@ -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
View File
@@ -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
View File
@@ -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!()
}
}