Skip to content

Commit

Permalink
Add fractional suppprt with new FrameRate type, merge SourceFrameForm…
Browse files Browse the repository at this point in the history
…at and FrameFormat
  • Loading branch information
l1npengtul committed Oct 3, 2023
1 parent dbdb42b commit ae0d818
Show file tree
Hide file tree
Showing 8 changed files with 4,169 additions and 99 deletions.
3,984 changes: 3,984 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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"
Expand Down
3 changes: 1 addition & 2 deletions nokhwa-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down
3 changes: 2 additions & 1 deletion nokhwa-core/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
})?;
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions nokhwa-core/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`]
Expand Down
80 changes: 1 addition & 79 deletions nokhwa-core/src/frame_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub enum FrameFormat {

// Custom
Custom(u128),
PlatformSpecificCustomFormat(PlatformFrameFormat),
}

impl FrameFormat {
Expand Down Expand Up @@ -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
126 changes: 115 additions & 11 deletions nokhwa-core/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
error::NokhwaError,
frame_format::{FrameFormat, SourceFrameFormat},
frame_format::{FrameFormat},
};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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),
}
}
}
Expand Down
Loading

0 comments on commit ae0d818

Please sign in to comment.