intermidiary commit

This commit is contained in:
l1npengtul
2024-11-11 18:28:56 +09:00
parent cc04c54299
commit ce695adf19
25 changed files with 675 additions and 1041 deletions
+5 -1
View File
@@ -22,7 +22,7 @@ test-fail-warnings = []
[dependencies]
thiserror = "1.0"
thiserror = "2.0"
bytes = "1.3"
paste = "1.0"
@@ -60,6 +60,10 @@ optional = true
version = "0.4"
optional = true
[dependencies.futures]
version = "0.3"
optional = true
[dependencies.rgb]
version = "0.8"
+33 -23
View File
@@ -1,42 +1,52 @@
use std::collections::HashMap;
use crate::buffer::Buffer;
use crate::controls::{CameraProperties, CameraPropertyId, CameraPropertyValue};
use crate::error::NokhwaError;
use crate::types::{CameraFormat, CameraIndex, Resolution};
use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue};
use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_format::FrameFormat;
use crate::stream::CaptureStream;
use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution};
pub trait Open {
fn open(index: CameraIndex) -> Self;
fn open(index: CameraIndex) -> NokhwaResult<Self>;
}
#[cfg(feature = "async")]
pub trait AsyncOpen {
async fn open_async(index: CameraIndex) -> Self;
async fn open_async(index: CameraIndex) -> NokhwaResult<Self>;
}
macro_rules! def_camera_props {
( $($property:ident, )* ) => {
$(
fn paste::paste! { [<$property:snake>] } (&self) -> Option<&CameraPropertyDescriptor> {
self.properties().paste::paste! { [<$property:snake>] }
paste::paste! {
$(
fn [<$property:snake>] (&self) -> Option<&CameraPropertyDescriptor> {
self.properties().[<$property:snake>]
}
fn [<set_ $property:snake>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.properties().[<set_ $property:snake >](value)
}
)*
}
fn paste::paste! { [<set_ $property:snake>] } (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError>;
)*
};
}
macro_rules! def_camera_props_async {
( $($property:ident, )* ) => {
$(
async fn paste::paste! { [<set_ $property:snake>] } (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError>;
)*
paste::paste! {
$(
async fn [<set_ $property:snake _async>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.properties().[<set_ $property:snake >](value)
}
)*
}
};
}
pub trait Setting {
fn enumerate_formats(&self) -> Vec<CameraFormat>;
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
fn enumerate_formats_by_resolution(&self) -> HashMap<Resolution, CameraFormat>;
fn enumerate_resolution_and_frame_rates(&self, frame_format: FrameFormat) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
@@ -66,9 +76,9 @@ pub trait Setting {
#[cfg(feature = "async")]
pub trait AsyncSetting {
async fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn set_property(&mut self, property: &CameraPropertyId, value: CameraPropertyValue) -> Result<(), NokhwaError>;
async fn set_property_async(&mut self, property: &CameraPropertyId, value: CameraPropertyValue) -> Result<(), NokhwaError>;
def_camera_props_async!(
Brightness,
@@ -91,10 +101,8 @@ pub trait AsyncSetting {
}
pub trait Stream {
fn open_stream(&mut self) -> Result<(), NokhwaError>;
fn poll_frame(&mut self) -> Result<Buffer, NokhwaError>;
fn open_stream(&mut self) -> Result<CaptureStream, NokhwaError>;
fn close_stream(&mut self) -> Result<(), NokhwaError>;
}
@@ -102,7 +110,7 @@ pub trait Stream {
pub trait AsyncStream {
async fn open_stream(&mut self) -> Result<(), NokhwaError>;
async fn poll_frame(&mut self) -> Result<Buffer, NokhwaError>;
async fn await_frame(&mut self) -> Result<Buffer, NokhwaError>;
async fn close_stream(&mut self) -> Result<(), NokhwaError>;}
@@ -110,3 +118,5 @@ pub trait Capture: Open + Setting + Stream {}
#[cfg(feature = "async")]
pub trait AsyncCapture: Capture + AsyncOpen + AsyncSetting + AsyncStream {}
+5 -5
View File
@@ -9,9 +9,9 @@ pub struct GeneralPurposeDecoder<D> where D: PixelWithColorType;
impl<D> Decoder for GeneralPurposeDecoder<D> where D: PixelWithColorType {
const ALLOWED_FORMATS: &'static [FrameFormat] = &[
FrameFormat::MJpeg, FrameFormat::Luma8, FrameFormat::Luma16, FrameFormat::Rgb8, FrameFormat::RgbA8,
FrameFormat::MJpeg, FrameFormat::Luma8, FrameFormat::Luma16, FrameFormat::Rgb332, FrameFormat::RgbA8888,
FrameFormat::Nv12, FrameFormat::Nv21, FrameFormat::Uyvy422, FrameFormat::Yuy2_422, FrameFormat::Yv12,
FrameFormat::Yuv444, FrameFormat::I420, FrameFormat::I422, FrameFormat::I444
FrameFormat::Ayuv444, FrameFormat::I420, FrameFormat::I422, FrameFormat::I444
];
type OutputPixels = D;
@@ -41,14 +41,14 @@ impl<D> Decoder for GeneralPurposeDecoder<D> where D: PixelWithColorType {
FrameFormat::MJpeg => PixelFormat::Rgb, // => JPEG decoder
FrameFormat::Yuy2_422 => PixelFormat::I422,
FrameFormat::Uyvy422 => PixelFormat::I422,
FrameFormat::Yuv444 => PixelFormat::I444,
FrameFormat::Ayuv444 => PixelFormat::I444,
FrameFormat::Nv12 => PixelFormat::Nv12,
FrameFormat::Nv21 => PixelFormat::Nv12,
FrameFormat::Yv12 => PixelFormat::I420,
FrameFormat::I420 => PixelFormat::I420,
// already decoded
FrameFormat::Rgb8 => PixelFormat::Rgb,
FrameFormat::RgbA8 => {
FrameFormat::Rgb332 => PixelFormat::Rgb,
FrameFormat::RgbA8888 => {
PixelFormat::Rgb
}
_ => return Err(()),
+2
View File
@@ -17,6 +17,8 @@
use crate::{frame_format::FrameFormat, types::ApiBackend};
use thiserror::Error;
pub type NokhwaResult<T> = Result<T, NokhwaError>;
/// All errors in `nokhwa`.
#[allow(clippy::module_name_repetitions)]
#[derive(Error, Debug, Clone)]
+70 -71
View File
@@ -21,12 +21,14 @@ use crate::types::ApiBackend;
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum FrameFormat {
// Compressed Formats
H265,
H264,
H263,
Avc1,
H263,
Av1,
Mpeg1,
Mpeg2,
Mpeg4,
@@ -35,40 +37,60 @@ pub enum FrameFormat {
VP8,
VP9,
// YCbCr formats
Yuv444,
// YCbCr Formats
// -> 422 16 BPP
Yuyv422,
Uyvy422,
// 8 bit per pixel, 4:4:4
// 420
Ayuv444,
// -> 4:2:2
Yuyv422, // AKA YUY2
Uyvy422, // UYUV
Yvyu422,
Yv12,
// 4:2:0
Nv12,
Nv21,
Yv12,
I420,
I422,
I444,
// 16:1:1
Yvu9,
// Grayscale Formats
Luma8,
Luma16,
// Depth
Depth16,
// RGB Formats
Rgb8,
RgbA8,
Rgb332,
Rgb555,
Rgb565,
Rgb888,
RgbA8888,
ARgb8888,
// Bayer Formats
Bayer8,
Bayer16,
// Custom
Custom(u128),
PlatformSpecificCustomFormat(PlatformSpecific),
Custom([u8; 8]),
}
// FIXME: Fix these frame format lists! Maybe move to using a macro..?
impl FrameFormat {
pub const ALL: &'static [FrameFormat] = &[
FrameFormat::H263,
FrameFormat::H264,
FrameFormat::H265,
FrameFormat::Av1,
FrameFormat::Avc1,
FrameFormat::Mpeg1,
FrameFormat::Mpeg2,
@@ -84,14 +106,15 @@ impl FrameFormat {
FrameFormat::Yv12,
FrameFormat::Luma8,
FrameFormat::Luma16,
FrameFormat::Rgb8,
FrameFormat::RgbA8,
FrameFormat::Rgb332,
FrameFormat::RgbA8888,
];
pub const COMPRESSED: &'static [FrameFormat] = &[
FrameFormat::H263,
FrameFormat::H264,
FrameFormat::H265,
FrameFormat::Av1,
FrameFormat::Avc1,
FrameFormat::Mpeg1,
FrameFormat::Mpeg2,
@@ -112,12 +135,13 @@ impl FrameFormat {
pub const LUMA: &'static [FrameFormat] = &[FrameFormat::Luma8, FrameFormat::Luma16];
pub const RGB: &'static [FrameFormat] = &[FrameFormat::Rgb8, FrameFormat::RgbA8];
pub const RGB: &'static [FrameFormat] = &[FrameFormat::Rgb332, FrameFormat::RgbA8888];
pub const COLOR_FORMATS: &'static [FrameFormat] = &[
FrameFormat::H265,
FrameFormat::H264,
FrameFormat::H263,
FrameFormat::Av1,
FrameFormat::Avc1,
FrameFormat::Mpeg1,
FrameFormat::Mpeg2,
@@ -131,8 +155,8 @@ impl FrameFormat {
FrameFormat::Nv12,
FrameFormat::Nv21,
FrameFormat::Yv12,
FrameFormat::Rgb8,
FrameFormat::RgbA8,
FrameFormat::Rgb332,
FrameFormat::RgbA8888,
];
pub const GRAYSCALE: &'static [FrameFormat] = &[FrameFormat::Luma8, FrameFormat::Luma16];
@@ -144,55 +168,30 @@ impl Display for FrameFormat {
}
}
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PlatformSpecific {
backend: ApiBackend,
format: u128,
}
impl PlatformSpecific {
#[must_use]
pub fn new(backend: ApiBackend, format: u128) -> Self {
Self { backend, format }
}
#[must_use]
pub fn backend(&self) -> ApiBackend {
self.backend
}
#[must_use]
pub fn format(&self) -> u128 {
self.format
}
#[must_use]
pub fn as_tuple(&self) -> (ApiBackend, u128) {
(self.backend, self.format)
}
}
impl From<(ApiBackend, u128)> for PlatformSpecific {
fn from(value: (ApiBackend, u128)) -> Self {
PlatformSpecific::new(value.0, value.1)
}
}
impl From<PlatformSpecific> for (ApiBackend, u128) {
fn from(value: PlatformSpecific) -> Self {
value.as_tuple()
}
}
impl PartialEq<(ApiBackend, u128)> for PlatformSpecific {
fn eq(&self, other: &(ApiBackend, u128)) -> bool {
&self.as_tuple() == other
}
}
impl Display for PlatformSpecific {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
#[macro_export]
macro_rules! define_back_and_fourth_frame_format {
($fourcc_type:ty, { $( $frame_format:expr => $value:literal, )* }, $func_u8_8_to_fcc:expr, $func_fcc_to_u8_8:expr, $value_to_fcc_type:expr) => {
pub struct FrameFormatIntermediate(pub $fourcc_type);
impl FrameFormatIntermediate {
pub fn from_frame_format(frame_format: FrameFormat) -> Option<Self> {
match frame_format {
$(
$frame_format => Some(Self($value_to_fcc_type($value))),
)*
FrameFormat::Custom(cv) => Some($func_u8_8_to_fcc(cv))
_ => None,
}
}
pub fn into_frame_format(fourcc: $fourcc_type) -> FrameFormat {
match fourcc.0 {
$(
$value => $frame_format,
)*
cv => FrameFormat::Custom($func_fcc_to_u8_8(cv)),
}
}
}
};
}
+6 -2
View File
@@ -31,5 +31,9 @@ pub mod types;
pub mod decoders;
pub mod utils;
pub mod ranges;
pub mod controls;
mod capture;
pub mod properties;
pub mod capture;
pub mod query;
#[cfg(feature = "async")]
pub mod stream;
@@ -5,7 +5,7 @@ use std::{
};
use std::cmp::Ordering;
use crate::error::NokhwaError;
use crate::ranges::{ArrayRange, IndicatedRange, KeyValue, Options, Range, RangeValidationFailure, Simple, ValidatableRange};
use crate::ranges::{ArrayRange, IndicatedRange, KeyValue, Options, RangeValidationFailure, Simple, ValidatableRange};
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ControlValidationFailure;
@@ -26,7 +26,7 @@ pub enum CameraPropertyId {
Gamma,
WhiteBalance,
BacklightCompensation,
Gain,
ISO,
Pan,
Tilt,
Zoom,
@@ -48,16 +48,18 @@ pub struct CameraProperties {
macro_rules! def_camera_props {
( $($property:ident, )* ) => {
impl CameraProperties {
$(
pub fn paste::paste! { [<$property:snake>] } (&self) -> Option<&CameraPropertyDescriptor> {
self.props.get(&CameraPropertyId::$property)
paste::paste! {
impl CameraProperties {
$(
pub fn [<$property:snake>] (&self) -> Option<&CameraPropertyDescriptor> {
self.props.get(&CameraPropertyId::$property)
}
pub fn [<set_ $property:snake>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.props.set_property(&CameraPropertyId::$property, value)
}
)*
}
pub fn paste::paste! { [<set_ $property:snake>] } (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.props.set_property(&CameraPropertyId::$property, value)
}
)*
}
};
}
@@ -71,7 +73,7 @@ def_camera_props!(
Gamma,
WhiteBalance,
BacklightCompensation,
Gain,
ISO,
Pan,
Tilt,
Zoom,
@@ -107,38 +109,34 @@ impl CameraProperties {
#[derive(Clone, Debug)]
pub struct CameraPropertyDescriptor {
flags: HashSet<CameraPropertyFlag>,
mode: CameraPropertyMode,
range: CameraPropertyRange,
value: CameraPropertyValue,
value_type: CameraPropertyValueType,
}
impl CameraPropertyDescriptor {
pub fn new(flags: &[CameraPropertyFlag], range: CameraPropertyRange, value: CameraPropertyValue) -> Self {
CameraPropertyDescriptor {
pub fn new(flags: &[CameraPropertyFlag], mode: CameraPropertyMode, range: CameraPropertyRange, value: CameraPropertyValue, value_type: CameraPropertyValueType) -> Result<Self, NokhwaError> {
if flags.contains(&CameraPropertyFlag::ReadOnly) && flags.contains(&CameraPropertyFlag::WriteOnly) {
return Err(NokhwaError::StructureError { structure: "CameraPropertyDescriptor".to_string(), error: "conflicting flags".to_string() })
}
Ok(CameraPropertyDescriptor {
flags: HashSet::from(flags),
mode,
range,
value,
}
value_type,
})
}
pub fn is_read_only(&self) -> Result<(), NokhwaError> {
if self.flags.contains(&CameraPropertyFlag::ReadOnly) {
return Err(NokhwaError::SetPropertyError {
property: "Flag".to_string(),
value: "N/A".to_string(),
error: "Read Only".to_string(),
})
}
Ok(())
pub fn is_read_only(&self) -> bool {
self.flags.contains(&CameraPropertyFlag::ReadOnly)
}
pub fn is_write_only(&self) -> Result<(), NokhwaError> {
if self.flags.contains(&CameraPropertyFlag::WriteOnly) {
return Err(NokhwaError::GetPropertyError {
property: "Flag".to_string(),
error: "Write Only".to_string(),
})
}
Ok(())
pub fn is_write_only(&self) -> bool {
self.flags.contains(&CameraPropertyFlag::WriteOnly)
}
pub fn is_disabled(&self) -> Result<(), NokhwaError> {
@@ -153,6 +151,14 @@ impl CameraPropertyDescriptor {
Ok(&self.flags)
}
pub fn mode(&self) -> CameraPropertyMode {
self.mode
}
pub fn value_type(&self) -> CameraPropertyValueType {
self.value_type
}
pub fn range(&self) -> &CameraPropertyRange {
&self.range
}
@@ -181,16 +187,34 @@ pub enum CameraCustomPropertyPlatformId {
LongInteger(i128),
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum CameraPropertyMode {
///
None,
/// Automatically Set
Automatic,
/// Manually Set
Manual,
/// Continuously Set
Continuous,
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum CameraPropertyValueType {
/// Relative
Relative,
/// Absolute
Absolute,
/// Unknown/Unused/Not Applicable
None,
}
/// The flags that a camera property may have.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum CameraPropertyFlag {
/// This is automatically set - you need not interfere
Automatic,
/// This is manually set - you need to interfere
Manual,
/// The value is set continuously by the driver.
Continuous,
/// The value may only be read from - any attempts to change the value will error.
ReadOnly,
/// The value can only be written to.
@@ -618,3 +642,39 @@ impl Display for CameraPropertyValue {
write!(f, "{self:?}")
}
}
#[macro_export]
macro_rules! define_back_and_fourth_control {
($id_type:ty, { $( $control:expr [ $mode:expr, $value_type:expr, $value:expr ] => $control_id:expr, )* }, $id_to_str:expr, $str_to_id:expr) => {
pub struct ControlIntermediate {
pub mode: Option<$id_type>,
pub value_type: Option<$id_type>,
pub value: $id_type,
}
impl ControlIntermediate {
pub fn
}
// impl ControlIntermediate {
// pub fn into_control(native_ctrl: $id_type) -> (crate::properties::CameraPropertyId, Option<crate::properties::CameraPropertyFlag>) {
// match native_ctrl {
// $(
// $control_id => ($control, $flag)
// )*
// nc => (crate::properties::CameraPropertyId::Custom($id_to_str(nc)), None)
// }
// }
//
// pub fn into_native(property_id: &crate::properties::CameraPropertyId, flag: Option<crate::properties::CameraPropertyFlag>) -> Option<$id_type> {
// match (property_id, flag) {
// $(
// ($control, $flag) => Some($control_id),
// )*
// (crate::properties::CameraPropertyId::Custom(str_id), _) => $str_to_id(str_id),
// _ => None,
// }
// }
// }
};
}
+10
View File
@@ -0,0 +1,10 @@
use crate::error::NokhwaError;
use crate::types::CameraInfo;
pub trait Query {
fn query() -> Result<Vec<CameraInfo>, NokhwaError>;
}
pub trait AsyncQuery {
async fn query() -> Result<Vec<CameraInfo>, NokhwaError>;
}
+43
View File
@@ -0,0 +1,43 @@
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll},
sync::mpsc::Receiver
};
use futures::Stream as AsyncStreamTrait;
use crate::{
buffer::Buffer,
error::NokhwaError
};
#[derive(Clone, Debug)]
pub enum ChannelState {
Frame(Buffer),
Error(NokhwaError),
ClosedWithError(NokhwaError),
Closed,
}
pub enum StreamType {
Channel(Arc<Receiver<ChannelState>>),
Callback(Arc<Mutex<Option<ChannelState>>>),
}
pub struct CaptureStream {}
impl Future for CaptureStream {
type Output = ChannelState;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
todo!()
}
}
impl AsyncStreamTrait for CaptureStream {
type Item = ChannelState;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
}
}
+46 -66
View File
@@ -98,8 +98,8 @@ impl TryFrom<CameraIndex> for usize {
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct Resolution {
pub width_x: u32,
pub height_y: u32,
width_x: u32,
height_y: u32,
}
impl Resolution {
@@ -180,97 +180,77 @@ impl Distance<u32> for Resolution {
}
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct FrameRate(pub f32);
#[derive(Copy, Clone, Debug, Hash)]
pub struct FrameRate {
numerator: u32,
denominator: u32
}
impl FrameRate {
#[must_use]
pub fn new(fps: f32) -> Self {
Self(fps)
pub fn new(numerator: u32, denominator: u32) -> Self {
Self {
numerator,
denominator,
}
}
#[must_use]
pub fn frame_rate(&self) -> f32 {
self.0
pub fn frame_rate(fps: u32) -> Self {
Self {
numerator: fps,
denominator: 1,
}
}
}
impl Deref for FrameRate {
type Target = f32;
fn deref(&self) -> &Self::Target {
&self.0
pub fn numerator(&self) -> u32 {
self.numerator
}
}
impl DerefMut for FrameRate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
pub fn denominator(&self) -> u32 {
self.denominator
}
}
impl Hash for FrameRate {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u32(self.0.to_bits());
}
}
impl Default for FrameRate {
fn default() -> Self {
FrameRate(30.0)
FrameRate::new(30, 1)
}
}
impl Display for FrameRate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} FPS", self.0)
write!(f, "{}/{} FPS", self.numerator, self.denominator)
}
}
impl Add for FrameRate {
type Output = FrameRate;
impl Eq for FrameRate {}
fn add(self, rhs: Self) -> Self::Output {
(self.0 + rhs.0).into()
impl PartialEq<Self> for FrameRate {
fn eq(&self, other: &Self) -> bool {
(self.numerator * other.denominator) == (other.numerator * self.numerator)
}
}
impl Add for &FrameRate {
type Output = FrameRate;
fn add(self, rhs: Self) -> Self::Output {
(self.0 + rhs.0).into()
impl PartialOrd<Self> for FrameRate {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for FrameRate {
fn cmp(&self, other: &Self) -> Ordering {
if self.numerator == 0 && other.denominator == 0 {
return Ordering::Equal;
}
if self.denominator == other.denominator {
return self.numerator.cmp(&other.numerator);
}
impl Sub for FrameRate {
type Output = FrameRate;
fn sub(self, rhs: Self) -> Self::Output {
(self.0 - rhs.0).into()
(self.numerator * other.denominator).cmp(&(other.numerator * self.denominator))
}
}
impl Sub for &FrameRate {
type Output = FrameRate;
fn sub(self, rhs: Self) -> Self::Output {
(self.0 - rhs.0).into()
}
}
impl From<f32> for FrameRate {
fn from(value: f32) -> Self {
Self(value)
}
}
impl From<FrameRate> for f32 {
fn from(value: FrameRate) -> Self {
value.0
}
}
/// This is a convenience struct that holds all information about the format of a webcam stream.
/// It consists of a [`Resolution`], [`FrameFormat`], and a [`FrameRate`].
@@ -357,7 +337,7 @@ impl Default for CameraFormat {
CameraFormat {
resolution: Resolution::new(640, 480),
format: FrameFormat::MJpeg,
frame_rate: FrameRate(30.),
frame_rate: FrameRate::default(),
}
}
}
@@ -392,12 +372,12 @@ impl CameraInfo {
// 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: &str, description: &str, misc: &str, index: &CameraIndex) -> Self {
pub fn new(human_name: String, description: String, misc: String, index: CameraIndex) -> Self {
CameraInfo {
human_name: human_name.to_string(),
description: description.to_string(),
misc: misc.to_string(),
index: index.clone(),
human_name,
description,
misc,
index,
}
}