mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 10:37:26 +00:00
untested ffmpeg decoder, mostly finished core
This commit is contained in:
@@ -13,7 +13,7 @@ repository = "https://github.com/l1npengtul/nokhwa"
|
||||
[features]
|
||||
default = []
|
||||
serialize = ["serde"]
|
||||
wgpu-types = ["wgpu"]
|
||||
wgpu = ["wgpu-types"]
|
||||
opencv-mat = ["opencv", "opencv/clang-runtime"]
|
||||
docs-features = ["serialize", "wgpu-types"]
|
||||
async = ["async-trait", "flume/async", "futures-core"]
|
||||
@@ -27,6 +27,8 @@ num-traits = "0.2"
|
||||
ordered-float = "5"
|
||||
typed-builder = "0.21"
|
||||
compact_str = "0.9"
|
||||
bytemuck = "1.23"
|
||||
smallmap = "1.4"
|
||||
|
||||
[dependencies.num-rational]
|
||||
version = "0.4"
|
||||
@@ -37,17 +39,12 @@ features = ["serde", "std"]
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
|
||||
[dependencies.small-map]
|
||||
version = "0.1.3"
|
||||
default-features = false
|
||||
features = ["fxhash"]
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu]
|
||||
[dependencies.wgpu-types]
|
||||
version = "25"
|
||||
optional = true
|
||||
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
use crate::control::{ControlDescription, ControlId, ControlValue, Controls};
|
||||
use crate::control::{ControlDescription, ControlId, ControlValue};
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::stream::{StreamConfiguration, StreamHandle};
|
||||
use crate::types::{CameraFormat, FrameRate, Resolution};
|
||||
use std::collections::hash_map::{Keys, Values};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::{Keys, Values};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait Setting {
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
fn enumerate_resolution_and_frame_rates(
|
||||
&self,
|
||||
frame_format: FrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
fn control_ids(&self) -> Keys<ControlId, ControlDescription>;
|
||||
@@ -27,9 +33,13 @@ pub trait Setting {
|
||||
|
||||
fn control_description(&self, id: &ControlId) -> Option<&ControlDescription>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
fn set_control(&mut self, property: &ControlId, value: ControlValue)
|
||||
-> Result<(), NokhwaError>;
|
||||
-> Result<(), NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
fn refresh_controls(&mut self) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
@@ -43,9 +53,7 @@ pub trait AsyncSetting {
|
||||
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
||||
|
||||
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
async fn properties_async(&self) -> &Controls;
|
||||
|
||||
|
||||
async fn set_property_async(
|
||||
&mut self,
|
||||
property: &ControlId,
|
||||
@@ -55,15 +63,25 @@ pub trait AsyncSetting {
|
||||
|
||||
pub trait Capture {
|
||||
/// Implementations MUST guarantee that there can only ever be one stream open at once.
|
||||
fn open_stream(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<Arc<StreamHandle>, NokhwaError>;
|
||||
/// # Errors
|
||||
/// Errors are driver specific
|
||||
fn open_stream(
|
||||
&mut self,
|
||||
stream_configuration: Option<StreamConfiguration>,
|
||||
) -> Result<Arc<StreamHandle>, NokhwaError>;
|
||||
|
||||
// Implementations MUST be multi-close tolerant.
|
||||
/// # Errors
|
||||
/// Errors are driver specific
|
||||
fn close_stream(&mut self) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub trait AsyncStream {
|
||||
async fn open_stream_async(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<StreamHandle, NokhwaError>;
|
||||
async fn open_stream_async(
|
||||
&mut self,
|
||||
stream_configuration: Option<StreamConfiguration>,
|
||||
) -> Result<StreamHandle, NokhwaError>;
|
||||
|
||||
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::types::CameraFormat;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Codec {
|
||||
type Config: Clone + Debug;
|
||||
|
||||
type Input<'a>;
|
||||
|
||||
type Output;
|
||||
|
||||
type WrittenMeta: Clone + Debug;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
fn allowed_formats(&self) -> Result<&[FrameFormat], NokhwaError>;
|
||||
|
||||
fn config(&self) -> &Self::Config;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
fn send_item(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError>;
|
||||
|
||||
fn receive_decoded_item(
|
||||
&mut self,
|
||||
writing_output: &mut Self::Output,
|
||||
) -> Result<Self::WrittenMeta, NokhwaError>;
|
||||
|
||||
fn preferred_buffer_min_size(
|
||||
&mut self,
|
||||
format: &Option<CameraFormat>,
|
||||
) -> Result<Option<usize>, NokhwaError>;
|
||||
|
||||
fn deinitialize(&mut self) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub trait CodecAsync: Codec {
|
||||
|
||||
async fn allowed_formats_async(&self) -> Result<&[FrameFormat], NokhwaError> {
|
||||
self.allowed_formats()
|
||||
}
|
||||
|
||||
async fn set_format_async(&self, format: CameraFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
async fn config_async(&self) -> &Self::Config {
|
||||
self.config()
|
||||
}
|
||||
|
||||
fn set_config_async(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
|
||||
self.set_config(config)
|
||||
}
|
||||
|
||||
fn send_item_async(&mut self, input: &Self::Input) -> Result<(), NokhwaError>;
|
||||
|
||||
fn receive_decoded_item_async(
|
||||
&mut self,
|
||||
writing_output: &mut Self::Output,
|
||||
) -> Result<Option<usize>, NokhwaError>;
|
||||
|
||||
async fn deinitialize_async(&mut self) -> Result<(), NokhwaError> {
|
||||
self.deinitialize()
|
||||
}
|
||||
}
|
||||
+38
-38
@@ -1,5 +1,6 @@
|
||||
use crate::error::{NokhwaError, NokhwaResult};
|
||||
use crate::ranges::{Range, ValidatableRange};
|
||||
use compact_str::CompactString;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::collections::hash_map::{Keys, Values};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -60,11 +61,11 @@ pub struct Controls {
|
||||
|
||||
impl Controls {
|
||||
/// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls`
|
||||
pub fn new(
|
||||
#[must_use] pub fn new(
|
||||
device_controls: HashMap<ControlId, ControlDescription>,
|
||||
device_values: HashMap<ControlId, ControlValue>,
|
||||
) -> Option<Self> {
|
||||
for (id, value) in device_values.iter() {
|
||||
for (id, value) in &device_values {
|
||||
if let Some(description) = device_controls.get(id) {
|
||||
if !description.validate(value) {
|
||||
return None;
|
||||
@@ -78,11 +79,11 @@ impl Controls {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
#[must_use] pub fn empty() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn unchecked_new(
|
||||
#[must_use] pub fn unchecked_new(
|
||||
device_controls: HashMap<ControlId, ControlDescription>,
|
||||
device_values: HashMap<ControlId, ControlValue>,
|
||||
) -> Self {
|
||||
@@ -92,36 +93,39 @@ impl Controls {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
|
||||
#[must_use] pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
|
||||
self.descriptions.get(control_id)
|
||||
}
|
||||
|
||||
pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
|
||||
#[must_use] pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
|
||||
self.values.get(control_id)
|
||||
}
|
||||
|
||||
pub fn descriptions(&self) -> Values<ControlId, ControlDescription> {
|
||||
#[must_use] pub fn descriptions(&self) -> Values<ControlId, ControlDescription> {
|
||||
self.descriptions.values()
|
||||
}
|
||||
|
||||
pub fn values(&self) -> Values<ControlId, ControlValue> {
|
||||
#[must_use] pub fn values(&self) -> Values<ControlId, ControlValue> {
|
||||
self.values.values()
|
||||
}
|
||||
|
||||
pub fn ids(&self) -> Keys<ControlId, ControlDescription> {
|
||||
#[must_use] pub fn ids(&self) -> Keys<ControlId, ControlDescription> {
|
||||
self.descriptions.keys()
|
||||
}
|
||||
|
||||
pub fn validate(&self, control_id: &ControlId, value: &ControlValue) -> Result<bool, NokhwaError> {
|
||||
let description = match self.descriptions.get(control_id) {
|
||||
Some(desc) => desc,
|
||||
None => return Err(NokhwaError::GetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
}),
|
||||
};
|
||||
pub fn validate(
|
||||
&self,
|
||||
control_id: &ControlId,
|
||||
value: &ControlValue,
|
||||
) -> Result<bool, NokhwaError> {
|
||||
let Some(description) = self.descriptions.get(control_id) else {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
});
|
||||
};
|
||||
|
||||
if let None = self.values.get(control_id) {
|
||||
if !self.values.contains_key(control_id) {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
@@ -159,7 +163,7 @@ pub struct ControlDescription {
|
||||
}
|
||||
|
||||
impl ControlDescription {
|
||||
pub fn new(
|
||||
#[must_use] pub fn new(
|
||||
control_flags: HashSet<ControlFlags>,
|
||||
control_value_descriptor: ControlValueDescriptor,
|
||||
default_value: Option<ControlValue>,
|
||||
@@ -177,7 +181,7 @@ impl ControlDescription {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_unchecked(
|
||||
#[must_use] pub fn new_unchecked(
|
||||
control_flags: HashSet<ControlFlags>,
|
||||
control_value_descriptor: ControlValueDescriptor,
|
||||
default_value: Option<ControlValue>,
|
||||
@@ -189,15 +193,15 @@ impl ControlDescription {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> &HashSet<ControlFlags> {
|
||||
#[must_use] pub fn flags(&self) -> &HashSet<ControlFlags> {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
pub fn descriptor(&self) -> &ControlValueDescriptor {
|
||||
#[must_use] pub fn descriptor(&self) -> &ControlValueDescriptor {
|
||||
&self.descriptor
|
||||
}
|
||||
|
||||
pub fn default_value(&self) -> &Option<ControlValue> {
|
||||
#[must_use] pub fn default_value(&self) -> &Option<ControlValue> {
|
||||
&self.default_value
|
||||
}
|
||||
|
||||
@@ -209,7 +213,7 @@ impl ControlDescription {
|
||||
self.flags.remove(&flag)
|
||||
}
|
||||
|
||||
pub fn validate(&self, value: &ControlValue) -> bool {
|
||||
#[must_use] pub fn validate(&self, value: &ControlValue) -> bool {
|
||||
self.descriptor.validate(value)
|
||||
}
|
||||
}
|
||||
@@ -233,11 +237,11 @@ pub enum ControlValueDescriptor {
|
||||
Null,
|
||||
Integer(Range<i64>),
|
||||
BitMask,
|
||||
Float(Range<f64>),
|
||||
Float(Range<OrderedFloat<f64>>),
|
||||
String,
|
||||
Boolean,
|
||||
// Array of any values of singular type
|
||||
Array(ControlValueDescriptor),
|
||||
Array(Box<ControlValueDescriptor>),
|
||||
// Menu(Enum) of valid choices
|
||||
// The keys are valid choices,
|
||||
// the values represent what the choice is (usually a string or int).
|
||||
@@ -257,7 +261,7 @@ pub enum ControlValueDescriptor {
|
||||
}
|
||||
|
||||
impl ControlValueDescriptor {
|
||||
pub fn validate(&self, value: &ControlValue) -> bool {
|
||||
#[must_use] pub fn validate(&self, value: &ControlValue) -> bool {
|
||||
match self {
|
||||
ControlValueDescriptor::Null => {
|
||||
if let &ControlValue::Null = value {
|
||||
@@ -291,12 +295,12 @@ impl ControlValueDescriptor {
|
||||
}
|
||||
ControlValueDescriptor::Array(arr) => {
|
||||
if let &ControlValue::Array(_) = value {
|
||||
return arr.is_valid_value(value);
|
||||
return arr.validate(value);
|
||||
}
|
||||
}
|
||||
ControlValueDescriptor::Binary(size_limits) => {
|
||||
if let ControlValue::Binary(bin) = value {
|
||||
return size_limits.validate(bin.len() as u64);
|
||||
return size_limits.validate(&(bin.len() as u64));
|
||||
}
|
||||
}
|
||||
ControlValueDescriptor::Menu(choices) => {
|
||||
@@ -328,7 +332,7 @@ pub enum ControlValue {
|
||||
Integer(i64),
|
||||
BitMask(u64),
|
||||
Float(OrderedFloat<f64>),
|
||||
String(String),
|
||||
String(CompactString),
|
||||
Boolean(bool),
|
||||
Array(Vec<ControlValue>),
|
||||
Binary(Vec<u8>),
|
||||
@@ -338,9 +342,8 @@ pub enum ControlValue {
|
||||
}
|
||||
|
||||
impl ControlValue {
|
||||
pub fn is_primitive(&self) -> bool {
|
||||
match self {
|
||||
ControlValue::Null
|
||||
#[must_use] pub fn is_primitive(&self) -> bool {
|
||||
matches!(self, ControlValue::Null
|
||||
| ControlValue::Integer(_)
|
||||
| ControlValue::BitMask(_)
|
||||
| ControlValue::Float(_)
|
||||
@@ -348,9 +351,7 @@ impl ControlValue {
|
||||
| ControlValue::Boolean(_)
|
||||
| ControlValue::Binary(_)
|
||||
| ControlValue::Area { .. }
|
||||
| ControlValue::Orientation(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
| ControlValue::Orientation(_))
|
||||
}
|
||||
|
||||
// pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool {
|
||||
@@ -369,7 +370,7 @@ impl ControlValue {
|
||||
// false
|
||||
// }
|
||||
|
||||
pub fn same_type(&self, other: &ControlValue) -> bool {
|
||||
#[must_use] pub fn same_type(&self, other: &ControlValue) -> bool {
|
||||
match self {
|
||||
ControlValue::Null => {
|
||||
if let ControlValue::Null = other {
|
||||
@@ -426,7 +427,6 @@ impl ControlValue {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
false
|
||||
|
||||
+30
-55
@@ -1,59 +1,34 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_buffer::FrameBuffer;
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::stream::{StreamHandle};
|
||||
use crate::types::{CameraFormat, FrameRate, Resolution};
|
||||
use crate::types::CameraFormat;
|
||||
use std::fmt::Debug;
|
||||
pub use image::{ImageBuffer, Pixel, Primitive};
|
||||
use crate::image::{DecodedImage, NonFloatScalarWidth};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Decoder<'stream, Video> where
|
||||
Video: Codec {
|
||||
video: Video,
|
||||
stream: &'stream mut StreamHandle
|
||||
pub trait Decoder {
|
||||
type Config: Clone + Debug + TryFrom<CameraFormat>;
|
||||
type OutputMeta: Debug;
|
||||
|
||||
fn config(&self) -> &Self::Config;
|
||||
|
||||
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError>;
|
||||
|
||||
fn decode_to_buffer(
|
||||
&mut self,
|
||||
to_decode: FrameBuffer,
|
||||
buffer: impl AsMut<[u8]>,
|
||||
) -> Result<Self::OutputMeta, NokhwaError>;
|
||||
|
||||
fn decode_to_pixel_buffer<P: Pixel>(
|
||||
&mut self,
|
||||
to_decode: FrameBuffer,
|
||||
buffer: impl AsMut<[P::Subpixel]>,
|
||||
) -> Result<Self::OutputMeta, NokhwaError>
|
||||
where <P as Pixel>::Subpixel: NonFloatScalarWidth;
|
||||
|
||||
fn decode<P: Pixel>(
|
||||
&mut self,
|
||||
to_decode: FrameBuffer,
|
||||
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
|
||||
where <P as Pixel>::Subpixel: NonFloatScalarWidth;
|
||||
}
|
||||
|
||||
impl<'stream, Video> Decoder<'stream, Video> where Video: Codec {
|
||||
pub fn new(stream: &'stream mut StreamHandle, decoder: Video) -> Result<Self, NokhwaError> {
|
||||
let format = stream.format();
|
||||
|
||||
let mut decoder = decoder;
|
||||
decoder.initialize(format)?;
|
||||
Ok(Self { video: decoder, stream })
|
||||
}
|
||||
|
||||
pub fn
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[derive(Debug)]
|
||||
pub struct DecoderAsync<'stream, Video> where
|
||||
Video: CodecAsync {
|
||||
video: Video,
|
||||
stream_handle: &'stream mut StreamHandle
|
||||
}
|
||||
|
||||
pub trait Codec: Debug {
|
||||
const ALLOWED_FORMATS: &'static [FrameFormat];
|
||||
|
||||
fn initialize(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
fn stop(&mut self) -> Result<(), NokhwaError>;
|
||||
|
||||
fn frame_format(&self) -> Result<FrameFormat, NokhwaError>;
|
||||
|
||||
fn resolution(&self) -> Result<Resolution, NokhwaError>;
|
||||
|
||||
fn frame_rate(&self) -> Result<FrameRate, NokhwaError>;
|
||||
|
||||
fn set_frame_format(&mut self, frame_format: FrameFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
fn set_resolution(&mut self, resolution: Resolution) -> Result<(), NokhwaError>;
|
||||
|
||||
fn set_frame_rate(&mut self, frame_rate: FrameRate) -> Result<(), NokhwaError>;
|
||||
|
||||
fn decode_frame(&mut self, buffer: &FrameBuffer) -> Result<Cow<'_, [u8]>, NokhwaError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub trait CodecAsync: Codec + Debug {}
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use crate::{frame_format::FrameFormat};
|
||||
use std::fmt::{Debug};
|
||||
use thiserror::Error;
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::platform::Backends;
|
||||
use std::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type NokhwaResult<T> = Result<T, NokhwaError>;
|
||||
|
||||
@@ -62,6 +62,8 @@ pub enum NokhwaError {
|
||||
ConversionError(String),
|
||||
#[error("Permission denied by user.")]
|
||||
PermissionDenied,
|
||||
#[error("Failed to decode: {0}")]
|
||||
Decoder(String),
|
||||
}
|
||||
//
|
||||
// pub enum InitializeError {}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::ranges::ValidatableRange;
|
||||
use crate::utils::Distance;
|
||||
use crate::{
|
||||
frame_format::FrameFormat,
|
||||
ranges::Range,
|
||||
types::{CameraFormat, FrameRate, Resolution},
|
||||
};
|
||||
use crate::ranges::ValidatableRange;
|
||||
|
||||
|
||||
/// A helper for choosing a [`CameraFormat`].
|
||||
/// The use of this is completely optional - for a simpler way try [`crate::camera::Camera::enumerate_formats`].
|
||||
@@ -16,7 +15,9 @@ pub enum FormatRequestType {
|
||||
/// Pick the closest [`CameraFormat`] to the one requested
|
||||
Closest {
|
||||
resolution: Option<Range<Resolution>>,
|
||||
preferred_resolution: Option<Resolution>,
|
||||
frame_rate: Option<Range<FrameRate>>,
|
||||
preferred_frame_rate: Option<FrameRate>,
|
||||
},
|
||||
HighestFrameRate {
|
||||
frame_rate: Range<FrameRate>,
|
||||
@@ -38,18 +39,21 @@ pub struct FormatRequest {
|
||||
}
|
||||
|
||||
impl FormatRequest {
|
||||
pub fn new(format_request_type: FormatRequestType, allowed_frame_formats: Vec<FrameFormat>) -> Self {
|
||||
#[must_use] pub fn new(
|
||||
format_request_type: FormatRequestType,
|
||||
allowed_frame_formats: Vec<FrameFormat>,
|
||||
) -> Self {
|
||||
Self {
|
||||
request_type: format_request_type,
|
||||
allowed_frame_formats,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn best<'a>(&self, camera_formats: &'a Vec<CameraFormat>) -> Option<&'a CameraFormat> {
|
||||
#[must_use] pub fn best<'a>(&self, camera_formats: &'a [CameraFormat]) -> Option<&'a CameraFormat> {
|
||||
camera_formats.first()
|
||||
}
|
||||
|
||||
pub fn sort_foramts(&self, mut camera_formats: Vec<CameraFormat>) -> Vec<CameraFormat> {
|
||||
#[must_use] pub fn sort_foramts(&self, mut camera_formats: Vec<CameraFormat>) -> Vec<CameraFormat> {
|
||||
if camera_formats.is_empty() {
|
||||
return camera_formats;
|
||||
}
|
||||
@@ -57,69 +61,61 @@ impl FormatRequest {
|
||||
match self.request_type {
|
||||
FormatRequestType::Closest {
|
||||
resolution,
|
||||
preferred_resolution,
|
||||
frame_rate,
|
||||
..
|
||||
preferred_frame_rate,
|
||||
} => {
|
||||
let resolution_point = resolution.map(|x| x.preferred());
|
||||
let frame_rate_point = frame_rate.map(|x| x.preferred());
|
||||
// lets calcuate distance in 3 dimensions (add both resolution and frame_rate together)
|
||||
|
||||
camera_formats.sort_by(|a, b| {
|
||||
let a_distance = format_distance_to_point(&resolution_point, &frame_rate_point, a);
|
||||
let b_distance = format_distance_to_point(&resolution_point, &frame_rate_point, b);
|
||||
let a_distance =
|
||||
format_distance_to_point(&preferred_resolution, &preferred_frame_rate, a);
|
||||
let b_distance =
|
||||
format_distance_to_point(&preferred_resolution, &preferred_frame_rate, b);
|
||||
|
||||
a_distance.total_cmp(&b_distance)
|
||||
});
|
||||
|
||||
camera_formats.into_iter().filter(|fmt| {
|
||||
self.allowed_frame_formats.contains(fmt.format())
|
||||
}).filter(|cam_fmt| {
|
||||
if let Some(res_range) = resolution {
|
||||
return res_range.validate(cam_fmt.resolution())
|
||||
}
|
||||
camera_formats
|
||||
.into_iter()
|
||||
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
|
||||
.filter(|cam_fmt| {
|
||||
if let Some(res_range) = resolution {
|
||||
return res_range.validate(cam_fmt.resolution());
|
||||
}
|
||||
|
||||
if let Some(frame_rate_range) = frame_rate {
|
||||
return frame_rate_range.validate(&cam_fmt.frame_rate())
|
||||
}
|
||||
true
|
||||
}).collect()
|
||||
if let Some(frame_rate_range) = frame_rate {
|
||||
return frame_rate_range.validate(cam_fmt.frame_rate());
|
||||
}
|
||||
true
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
FormatRequestType::HighestFrameRate {
|
||||
frame_rate
|
||||
} => {
|
||||
camera_formats.sort_by(|a, b| {
|
||||
a.frame_rate().cmp(b.frame_rate())
|
||||
});
|
||||
FormatRequestType::HighestFrameRate { frame_rate } => {
|
||||
camera_formats.sort_by(|a, b| a.frame_rate().cmp(b.frame_rate()));
|
||||
|
||||
camera_formats.into_iter().filter(|fmt| {
|
||||
self.allowed_frame_formats.contains(fmt.format())
|
||||
}).filter(|a| {
|
||||
frame_rate.validate(a.frame_rate())
|
||||
}).collect()
|
||||
camera_formats
|
||||
.into_iter()
|
||||
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
|
||||
.filter(|a| frame_rate.validate(a.frame_rate()))
|
||||
.collect()
|
||||
}
|
||||
FormatRequestType::HighestResolution {
|
||||
resolution
|
||||
} => {
|
||||
camera_formats.sort_by(|a, b| {
|
||||
a.resolution().cmp(b.resolution())
|
||||
});
|
||||
FormatRequestType::HighestResolution { resolution } => {
|
||||
camera_formats.sort_by(|a, b| a.resolution().cmp(b.resolution()));
|
||||
|
||||
camera_formats.into_iter().filter(|fmt| {
|
||||
self.allowed_frame_formats.contains(fmt.format())
|
||||
}).filter(|a| {
|
||||
resolution.validate(a.resolution())
|
||||
}).collect()
|
||||
camera_formats
|
||||
.into_iter()
|
||||
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
|
||||
.filter(|a| resolution.validate(a.resolution()))
|
||||
.collect()
|
||||
}
|
||||
FormatRequestType::Exact {
|
||||
resolution,
|
||||
frame_rate,
|
||||
} => {
|
||||
camera_formats.into_iter().filter(|fmt| {
|
||||
self.allowed_frame_formats.contains(fmt.format())
|
||||
}).filter(|a| {
|
||||
resolution.eq(a.resolution()) && frame_rate.eq(a.frame_rate())
|
||||
}).collect()
|
||||
}
|
||||
} => camera_formats
|
||||
.into_iter()
|
||||
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
|
||||
.filter(|a| resolution.eq(a.resolution()) && frame_rate.eq(a.frame_rate()))
|
||||
.collect(),
|
||||
FormatRequestType::Any => {
|
||||
// return as-is
|
||||
camera_formats
|
||||
@@ -128,14 +124,23 @@ impl FormatRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_distance_to_point(resolution: &Option<Resolution>, frame_rate: &Option<FrameRate>, format: &CameraFormat) -> f32 {
|
||||
#[must_use]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn format_distance_to_point(
|
||||
resolution: &Option<Resolution>,
|
||||
frame_rate: &Option<FrameRate>,
|
||||
format: &CameraFormat,
|
||||
) -> f32 {
|
||||
let frame_rate_distance = match frame_rate {
|
||||
Some(f_point) => (format.frame_rate() - f_point).approximate_float().unwrap_or(f32::INFINITY).abs(),
|
||||
Some(f_point) => (format.frame_rate() - f_point)
|
||||
.approximate_float()
|
||||
.unwrap_or(f32::INFINITY)
|
||||
.abs(),
|
||||
None => 0_f32,
|
||||
};
|
||||
|
||||
|
||||
let resolution_point_distance = match resolution {
|
||||
Some(res_pt) => format.resolution().distance_from(&res_pt) as f32,
|
||||
Some(res_pt) => format.resolution().distance_from(res_pt) as f32,
|
||||
None => 0_f32,
|
||||
};
|
||||
|
||||
|
||||
@@ -13,53 +13,56 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use crate::control::ControlValue;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use crate::frame_format::FrameFormat;
|
||||
use small_map::{FxSmallMap, Iter};
|
||||
use crate::control::ControlValue;
|
||||
|
||||
use std::ops::Deref;
|
||||
pub use compact_str::CompactString;
|
||||
pub use smallmap::Map;
|
||||
|
||||
pub type PlatformSpecificFlag = u32;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Metadata {
|
||||
flags: FxSmallMap<8, CompactString, ControlValue>,
|
||||
flags: Map<CompactString, ControlValue>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub fn new() -> Self {
|
||||
#[must_use] pub fn new() -> Self {
|
||||
Self {
|
||||
flags: Default::default(),
|
||||
flags: Map::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: CompactString) -> Option<&ControlValue> {
|
||||
self.flags.get(&key)
|
||||
#[must_use] pub fn get(&self, key: &str) -> Option<&ControlValue> {
|
||||
self.flags.get(key)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: CompactString, value: ControlValue) {
|
||||
self.flags.insert(key, value);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, 8, CompactString, ControlValue> {
|
||||
self.flags.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Metadata {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for (key, value) in self.flags {
|
||||
for (key, value) in self.flags.iter() {
|
||||
state.write(key.as_bytes());
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Metadata {
|
||||
type Target = Map<CompactString, ControlValue>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Metadata {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
for (this_key, this_value) in &self.flags {
|
||||
for (this_key, this_value) in self.flags.iter() {
|
||||
if let Some(other_value) = other.flags.get(this_key) {
|
||||
if this_value != other_value {
|
||||
return false;
|
||||
@@ -87,10 +90,7 @@ impl FrameBuffer {
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn new(buffer: Cow<'static, [u8]>, metadata: Option<Metadata>) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
metadata,
|
||||
}
|
||||
Self { buffer, metadata }
|
||||
}
|
||||
|
||||
/// Get the data of this buffer.
|
||||
@@ -99,8 +99,8 @@ impl FrameBuffer {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
pub fn consume(self) -> (Cow<'static, [u8]>, Option<Metadata>) {
|
||||
return (self.buffer, self.metadata)
|
||||
#[must_use] pub fn consume(self) -> (Cow<'static, [u8]>, Option<Metadata>) {
|
||||
(self.buffer, self.metadata)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
+307
-83
@@ -15,71 +15,70 @@
|
||||
*/
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use ordered_float::OrderedFloat;
|
||||
// /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
|
||||
// /// Note that endianness is determined by the native machine (or the driver itself).
|
||||
// #[derive(Clone, Debug, Hash, PartialOrd, PartialEq)]
|
||||
// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
// #[non_exhaustive]
|
||||
// pub enum FrameFormat {
|
||||
// // Compressed Formats
|
||||
//
|
||||
//
|
||||
// // YCbCr Formats
|
||||
//
|
||||
// // 8 bit per pixel, 4:4:4
|
||||
// Ayuv444,
|
||||
//
|
||||
// // -> 4:2:2
|
||||
// Yuyv422, // AKA YUY2
|
||||
// Uyvy422, // UYUV
|
||||
// Yvyu422,
|
||||
// Yv12,
|
||||
//
|
||||
// // 4:2:0
|
||||
// Nv12,
|
||||
// Nv21,
|
||||
// I420,
|
||||
//
|
||||
// // 16:1:1
|
||||
// Yvu9,
|
||||
//
|
||||
// // Grayscale Formats
|
||||
// Luma8,
|
||||
// Luma16,
|
||||
//
|
||||
// // Depth
|
||||
// Depth16,
|
||||
//
|
||||
// // RGB Formats
|
||||
// Rgb332,
|
||||
// Rgb888,
|
||||
// RgbA8888,
|
||||
// ARgb8888,
|
||||
// RgbX1010102,
|
||||
// RgbA1010102,
|
||||
// ARgb1010102,
|
||||
//
|
||||
//
|
||||
// Bgr888,
|
||||
// BgrA8888,
|
||||
// Bgr121212,
|
||||
// BgrA1212121212,
|
||||
//
|
||||
// Bgr161616,
|
||||
// Bgr16161616,
|
||||
//
|
||||
//
|
||||
// // Bayer Formats
|
||||
// Bayer8,
|
||||
// Bayer16,
|
||||
//
|
||||
// // Custom
|
||||
// Custom(CustomFrameFormat),
|
||||
// }
|
||||
|
||||
/// 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,
|
||||
Avc1,
|
||||
H263,
|
||||
Av1,
|
||||
Mpeg1,
|
||||
Mpeg2,
|
||||
Mpeg4,
|
||||
MJpeg,
|
||||
XVid,
|
||||
VP8,
|
||||
VP9,
|
||||
|
||||
// YCbCr Formats
|
||||
|
||||
// 8 bit per pixel, 4:4:4
|
||||
Ayuv444,
|
||||
|
||||
// -> 4:2:2
|
||||
Yuyv422, // AKA YUY2
|
||||
Uyvy422, // UYUV
|
||||
Yvyu422,
|
||||
Yv12,
|
||||
|
||||
// 4:2:0
|
||||
Nv12,
|
||||
Nv21,
|
||||
I420,
|
||||
|
||||
// 16:1:1
|
||||
Yvu9,
|
||||
|
||||
// Grayscale Formats
|
||||
Luma8,
|
||||
Luma16,
|
||||
|
||||
// Depth
|
||||
Depth16,
|
||||
|
||||
// RGB Formats
|
||||
Rgb332,
|
||||
Rgb888,
|
||||
|
||||
Bgr888,
|
||||
BgrA8888,
|
||||
|
||||
RgbA8888,
|
||||
ARgb8888,
|
||||
|
||||
// Bayer Formats
|
||||
Bayer8,
|
||||
Bayer16,
|
||||
|
||||
// Custom
|
||||
Custom([u8; 8]),
|
||||
}
|
||||
|
||||
macro_rules! define_frame_format_groups {
|
||||
macro_rules! define_frame_format_with_groups {
|
||||
(
|
||||
$(
|
||||
$group_name:ident => [
|
||||
@@ -87,50 +86,275 @@ macro_rules! define_frame_format_groups {
|
||||
]
|
||||
),* $(,)?
|
||||
) => {
|
||||
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
|
||||
/// Note that endianness is determined by the native machine (or the driver itself), unless otherwise
|
||||
/// specified.
|
||||
///
|
||||
/// Note that compatibility is driver dependant, while some drivers (such as Web) may not respect the setting
|
||||
/// at all.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialOrd, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[non_exhaustive]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum FrameFormat {
|
||||
$(
|
||||
$($format,)*
|
||||
)*
|
||||
Custom(CustomFrameFormat),
|
||||
}
|
||||
|
||||
impl FrameFormat {
|
||||
$(
|
||||
pub const $group_name: &'static [FrameFormat] = &[
|
||||
$(FrameFormat::$format),*
|
||||
];
|
||||
)*
|
||||
pub const ALL: &'static [FrameFormat] = &[
|
||||
$(
|
||||
$(FrameFormat::$format,)*
|
||||
)*
|
||||
];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_frame_format_groups! {
|
||||
ALL => [
|
||||
H263, H264, H265, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid,
|
||||
VP8, VP9, Yuyv422, Uyvy422, Nv12, Nv21, Yv12, Luma8, Luma16,
|
||||
Rgb332, RgbA8888
|
||||
],
|
||||
define_frame_format_with_groups! {
|
||||
COMPRESSED => [
|
||||
H263, H264, H265, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid,
|
||||
VP8, VP9
|
||||
H265,
|
||||
HEVC,
|
||||
H264,
|
||||
AVC1,
|
||||
H263,
|
||||
AV1,
|
||||
MPEG_1,
|
||||
MPEG_2,
|
||||
MPEG_4,
|
||||
MJPEG,
|
||||
XviD,
|
||||
VP8,
|
||||
VP9,
|
||||
],
|
||||
CHROMA => [
|
||||
Yuyv422, Uyvy422, Nv12, Nv21, Yv12
|
||||
|
||||
YCBCR_PACKED_444 => [
|
||||
Ayuv_32,
|
||||
],
|
||||
YCBCR_PLANAR_444 => [],
|
||||
YCBCR_SEMI_PLANAR_444 => [
|
||||
NV24,
|
||||
NV42,
|
||||
],
|
||||
YCBCR_PACKED_422 => [
|
||||
Yuyv_4_2_2,
|
||||
Uyvy_4_2_2,
|
||||
Vyuy_4_2_2,
|
||||
Yvyu_4_2_2,
|
||||
Y210,
|
||||
Y216,
|
||||
],
|
||||
YCBCR_PLANAR_422 => [],
|
||||
YCBCR_SEMI_PLANAR_422 => [
|
||||
NV16,
|
||||
NV61,
|
||||
],
|
||||
YCBCR_PACKED_420 => [],
|
||||
YCBCR_PLANAR_420 => [
|
||||
Yuv_4_2_0,
|
||||
Yvu_4_2_0,
|
||||
],
|
||||
YCBCR_SEMI_PLANAR_420 => [
|
||||
NV12,
|
||||
NV21,
|
||||
P010,
|
||||
P012,
|
||||
|
||||
],
|
||||
YCBCR_PACKED_411 => [
|
||||
Y41Packed
|
||||
],
|
||||
YCBCR_PLANAR_411 => [
|
||||
Y411Planar
|
||||
],
|
||||
YCBCR_SEMI_PLANAR_411 => [
|
||||
NV11,
|
||||
],
|
||||
|
||||
LUMA => [
|
||||
Luma8, Luma16
|
||||
Luma_8,
|
||||
Luma_10,
|
||||
Luma_12,
|
||||
Luma_14,
|
||||
Luma_16,
|
||||
Depth_16,
|
||||
],
|
||||
RGB => [
|
||||
Rgb332, RgbA8888
|
||||
|
||||
RAW_RGB => [
|
||||
Rgb_3_3_2,
|
||||
Rgb_5_6_5,
|
||||
Rgb_5_5_5,
|
||||
Rgb_8_8_8,
|
||||
Argb_8_8_8_8,
|
||||
Rgba_8_8_8_8,
|
||||
],
|
||||
COLOR_FORMATS => [
|
||||
H265, H264, H263, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid,
|
||||
VP8, VP9, Yuyv422, Uyvy422, Nv12, Nv21, Yv12, Rgb332, RgbA8888
|
||||
],
|
||||
GRAYSCALE => [
|
||||
Luma8, Luma16
|
||||
|
||||
RAW_BGR => [
|
||||
Bgr_3_3_2,
|
||||
Bgr_5_6_5,
|
||||
Bgr_5_5_5,
|
||||
Bgr_8_8_8,
|
||||
Abgr_8_8_8_8,
|
||||
Bgra_8_8_8_8,
|
||||
]
|
||||
}
|
||||
|
||||
// define_frame_format_groups! {
|
||||
// ALL => [
|
||||
// // Compressed Formats
|
||||
// H265,
|
||||
// H264,
|
||||
// Avc1,
|
||||
// H263,
|
||||
// Av1,
|
||||
// Mpeg1,
|
||||
// Mpeg2,
|
||||
// Mpeg4,
|
||||
// MJpeg,
|
||||
// XVid,
|
||||
// VP8,
|
||||
// VP9,
|
||||
//
|
||||
// // YCbCr Formats
|
||||
//
|
||||
// // 8 bit per pixel, 4:4:4
|
||||
// Ayuv444,
|
||||
//
|
||||
// // -> 4:2:2
|
||||
// Yuyv422, // AKA YUY2
|
||||
// Uyvy422, // UYUV
|
||||
// Yvyu422,
|
||||
// Yv12,
|
||||
//
|
||||
// // 4:2:0
|
||||
// Nv12,
|
||||
// Nv21,
|
||||
// I420,
|
||||
//
|
||||
// // 16:1:1
|
||||
// Yvu9,
|
||||
//
|
||||
// // Grayscale Formats
|
||||
// Luma8,
|
||||
// Luma16,
|
||||
//
|
||||
// // Depth
|
||||
// Depth16,
|
||||
//
|
||||
// // RGB Formats
|
||||
// Rgb332,
|
||||
// Rgb888,
|
||||
//
|
||||
// Bgr888,
|
||||
// BgrA8888,
|
||||
//
|
||||
// RgbA8888,
|
||||
// ARgb8888,
|
||||
//
|
||||
// // Bayer Formats
|
||||
// Bayer8,
|
||||
// Bayer16,
|
||||
// ],
|
||||
// COMPRESSED => [
|
||||
// H265,
|
||||
// H264,
|
||||
// Avc1,
|
||||
// H263,
|
||||
// Av1,
|
||||
// Mpeg1,
|
||||
// Mpeg2,
|
||||
// Mpeg4,
|
||||
// MJpeg,
|
||||
// XVid,
|
||||
// VP8,
|
||||
// VP9,
|
||||
// ],
|
||||
// YCBCR => [
|
||||
// Ayuv444,
|
||||
//
|
||||
// // -> 4:2:2
|
||||
// Yuyv422, // AKA YUY2
|
||||
// Uyvy422, // UYUV
|
||||
// Yvyu422,
|
||||
// Yv12,
|
||||
//
|
||||
// // 4:2:0
|
||||
// Nv12,
|
||||
// Nv21,
|
||||
// I420,
|
||||
//
|
||||
// // 16:1:1
|
||||
// Yvu9,
|
||||
// ],
|
||||
// YCBCR_PACKED => [
|
||||
// Ayuv444,
|
||||
//
|
||||
// // -> 4:2:2
|
||||
// Yuyv422, // AKA YUY2
|
||||
// Uyvy422, // UYUV
|
||||
// Yvyu422,
|
||||
//
|
||||
// // 4:2:0
|
||||
// Nv12,
|
||||
// Nv21,
|
||||
// I420,
|
||||
//
|
||||
// ],
|
||||
// YCBCR_PLANAR => [ Yvu9,
|
||||
// Yv12,
|
||||
// ],
|
||||
// LUMA => [
|
||||
// Luma8, Luma16
|
||||
// ],
|
||||
// RGB => [
|
||||
// Rgb332, RgbA8888
|
||||
// ],
|
||||
// COLOR_FORMATS => [
|
||||
// H265, H264, H263, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid,
|
||||
// VP8, VP9, Yuyv422, Uyvy422, Nv12, Nv21, Yv12, Rgb332, RgbA8888
|
||||
// ],
|
||||
// GRAYSCALE => [
|
||||
// Luma8, Luma16, Depth16,
|
||||
// ],
|
||||
// RAW => [
|
||||
// // RGB Formats
|
||||
// Rgb332,
|
||||
// Rgb888,
|
||||
//
|
||||
// Bgr888,
|
||||
// BgrA8888,
|
||||
//
|
||||
// RgbA8888,
|
||||
// ARgb8888,],
|
||||
// BAYER => [
|
||||
// // Bayer Formats
|
||||
// Bayer8,
|
||||
// Bayer16,],
|
||||
// }
|
||||
|
||||
impl Display for FrameFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialOrd, PartialEq)]
|
||||
pub enum CustomFrameFormat {
|
||||
UUID(u128),
|
||||
FourCC([char; 4]),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
F32(OrderedFloat<f32>),
|
||||
F64(OrderedFloat<f64>),
|
||||
}
|
||||
|
||||
#[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) => {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use image::{ImageBuffer, Pixel, Primitive};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use bytemuck::Pod;
|
||||
use num_traits::{NumCast, PrimInt};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DecodedImage<Px, Meta>
|
||||
where
|
||||
Px: Pixel,
|
||||
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
Meta: Debug,
|
||||
{
|
||||
pub buffer: ImageBuffer<Px, Vec<Px::Subpixel>>,
|
||||
pub metadata: Meta,
|
||||
}
|
||||
|
||||
impl<Px, Meta> DecodedImage<Px, Meta> where
|
||||
Px: Pixel,
|
||||
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
Meta: Debug {
|
||||
pub fn new(buffer: ImageBuffer<Px, Vec<Px::Subpixel>>,
|
||||
metadata: Meta) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Px, Meta> Deref for DecodedImage<Px, Meta>
|
||||
where
|
||||
Px: Pixel,
|
||||
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
|
||||
Meta: Debug
|
||||
{
|
||||
type Target = ImageBuffer<Px, Vec<Px::Subpixel>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<Px, Meta> DerefMut for DecodedImage<Px, Meta>
|
||||
where
|
||||
Px: Pixel,
|
||||
<Px as Pixel>::Subpixel: NonFloatScalarWidth,Meta: Debug
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
// not for safe work ;p
|
||||
// TODO: add more custom integer sizes, or break our dependence on image entirely and
|
||||
// create our own imagebuffer
|
||||
pub trait NonFloatScalarWidth: Debug + Primitive + PrimInt + NumCast + Pod {
|
||||
const WIDTH: u32;
|
||||
}
|
||||
|
||||
macro_rules! impl_nfsw {
|
||||
( $( [ ( $( $things:ty ),+ ) : $size:literal ] ),* $(,)? ) => {
|
||||
$(
|
||||
$(
|
||||
impl NonFloatScalarWidth for $things {
|
||||
const WIDTH: u32 = $size;
|
||||
}
|
||||
)+
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_nfsw! {
|
||||
[ (u8, i8) : 8 ],
|
||||
[ (u16, i16) : 16 ],
|
||||
[ (u32, i32) : 32 ],
|
||||
[ (u64, i64) : 64 ],
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
#![deny(clippy::pedantic)]
|
||||
#![warn(clippy::all)]
|
||||
#![cfg_attr(feature = "test-fail-warning", deny(warnings))]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![cfg_attr(feature = "test-fail-warnings", deny(warnings))]
|
||||
#![cfg_attr(feature = "docs-features", feature(doc_cfg))]
|
||||
/*
|
||||
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
|
||||
* Copyright 2025 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.
|
||||
@@ -20,15 +21,17 @@
|
||||
|
||||
//! Core type definitions for `nokhwa`
|
||||
pub mod camera;
|
||||
pub mod codec;
|
||||
pub mod control;
|
||||
pub mod decoder;
|
||||
pub mod error;
|
||||
pub mod format_request;
|
||||
pub mod frame_buffer;
|
||||
pub mod frame_format;
|
||||
pub mod control;
|
||||
pub mod image;
|
||||
pub mod platform;
|
||||
pub mod ranges;
|
||||
pub mod stream;
|
||||
pub mod traits;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
pub mod stream;
|
||||
pub mod platform;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::camera::{AsyncCamera, Camera};
|
||||
use crate::camera::Camera;
|
||||
use crate::error::NokhwaResult;
|
||||
use crate::types::{CameraIndex, CameraInformation};
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -15,7 +15,7 @@ pub enum Backends {
|
||||
|
||||
impl Display for Backends {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,15 @@ pub trait PlatformTrait {
|
||||
|
||||
fn open(&mut self, index: CameraIndex) -> NokhwaResult<Self::Camera>;
|
||||
|
||||
fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult<Box<dyn Camera>> {
|
||||
self.open(index).map(|cam| Box::new(cam))
|
||||
fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult<Box<dyn Camera>> where <Self as PlatformTrait>::Camera: 'static {
|
||||
self.open(index).map(|cam| Box::new(cam) as Box<dyn Camera>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub trait AsyncPlatformTrait {
|
||||
pub trait AsyncPlatformTrait: PlatformTrait {
|
||||
const PLATFORM: Backends;
|
||||
type AsyncCamera: AsyncCamera;
|
||||
type AsyncCamera: crate::camera::AsyncCamera;
|
||||
|
||||
async fn await_permission(&mut self) -> NokhwaResult<()>;
|
||||
|
||||
@@ -47,7 +47,7 @@ pub trait AsyncPlatformTrait {
|
||||
|
||||
async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
|
||||
|
||||
async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>> {
|
||||
self.open_async(index).await.map(|cam| Box::new(cam))
|
||||
async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>> where <Self as AsyncPlatformTrait>::AsyncCamera: 'static {
|
||||
self.open_async(index).await.map(|cam| Box::new(cam) as Box<dyn Camera>)
|
||||
}
|
||||
}
|
||||
|
||||
+52
-46
@@ -1,7 +1,7 @@
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::hash::Hash;
|
||||
use std::ops::{Div, Rem, Sub};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
/// A range type that can be validated.
|
||||
pub trait ValidatableRange {
|
||||
@@ -16,7 +16,9 @@ pub trait ValidatableRange {
|
||||
///
|
||||
/// Inclusive by default.
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Range<T> where T: RangeItem
|
||||
pub struct Range<T>
|
||||
where
|
||||
T: RangeItem,
|
||||
{
|
||||
minimum: T,
|
||||
lower_inclusive: bool,
|
||||
@@ -25,7 +27,10 @@ pub struct Range<T> where T: RangeItem
|
||||
step: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Range<T> where T: Copy {
|
||||
impl<T> Range<T>
|
||||
where
|
||||
T: Copy + RangeItem,
|
||||
{
|
||||
/// Create an upper and lower inclusive [`Range`]
|
||||
pub fn new(min: T, max: T, step: Option<T>) -> Self {
|
||||
Self {
|
||||
@@ -42,7 +47,7 @@ impl<T> Range<T> where T: Copy {
|
||||
lower_inclusive: bool,
|
||||
max: T,
|
||||
upper_inclusive: bool,
|
||||
step: Option<T>
|
||||
step: Option<T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
minimum: min,
|
||||
@@ -53,13 +58,13 @@ impl<T> Range<T> where T: Copy {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_minimum(&mut self, minimum: Option<T>) {
|
||||
pub fn set_minimum(&mut self, minimum: T) {
|
||||
self.minimum = minimum;
|
||||
}
|
||||
pub fn set_lower_inclusive(&mut self, lower_inclusive: bool) {
|
||||
self.lower_inclusive = lower_inclusive;
|
||||
}
|
||||
pub fn set_maximum(&mut self, maximum: Option<T>) {
|
||||
pub fn set_maximum(&mut self, maximum: T) {
|
||||
self.maximum = maximum;
|
||||
}
|
||||
pub fn set_upper_inclusive(&mut self, upper_inclusive: bool) {
|
||||
@@ -92,33 +97,27 @@ where
|
||||
type Validation = T;
|
||||
|
||||
fn validate(&self, value: &T) -> bool {
|
||||
let l_comparison_fn = match self.lower_inclusive {
|
||||
true => T::ge,
|
||||
false => T::gt,
|
||||
};
|
||||
let u_comparison_fn = match self.upper_inclusive {
|
||||
true => T::le,
|
||||
false => T::lt,
|
||||
};
|
||||
let l_comparison_fn = if self.lower_inclusive { T::ge } else { T::gt };
|
||||
let u_comparison_fn = if self.upper_inclusive { T::le } else { T::lt };
|
||||
|
||||
if !(l_comparison_fn(&self.minimum, value) && u_comparison_fn(&self.maximum, value)) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
// check step
|
||||
|
||||
if let Some(step) = self.step {
|
||||
let step_chk_value = *value - self.minimum;
|
||||
return step_chk_value % step == 0;
|
||||
return step_chk_value % step == T::ZERO;
|
||||
}
|
||||
|
||||
return true
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Range<T>
|
||||
where
|
||||
T: Default,
|
||||
T: Default + RangeItem,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Range {
|
||||
@@ -133,7 +132,7 @@ where
|
||||
|
||||
impl<T> Display for Range<T>
|
||||
where
|
||||
T: Debug,
|
||||
T: Debug + RangeItem,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let lower_inclusive_char = bool_to_inclusive_char(self.lower_inclusive, false);
|
||||
@@ -148,44 +147,46 @@ where
|
||||
}
|
||||
|
||||
fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char {
|
||||
match inclusive {
|
||||
true => {
|
||||
if upper {
|
||||
']'
|
||||
} else {
|
||||
'['
|
||||
}
|
||||
}
|
||||
false => {
|
||||
if upper {
|
||||
')'
|
||||
} else {
|
||||
'('
|
||||
}
|
||||
if inclusive {
|
||||
if upper {
|
||||
']'
|
||||
} else {
|
||||
'['
|
||||
}
|
||||
} else if upper {
|
||||
')'
|
||||
} else {
|
||||
'('
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn default_to_string<T>(default: &Option<T>) -> String
|
||||
where
|
||||
T: Debug,
|
||||
pub trait RangeItem:
|
||||
Copy
|
||||
+ Clone
|
||||
+ Debug
|
||||
+ Div<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Rem<Output = Self>
|
||||
+ Hash
|
||||
+ Ord
|
||||
+ PartialOrd
|
||||
+ Eq
|
||||
+ PartialEq
|
||||
{
|
||||
match default {
|
||||
Some(v) => {
|
||||
format!("{v:?}")
|
||||
}
|
||||
None => String::from("None"),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RangeItem: Copy + Clone + Debug + Div<Output = Self> + Sub<Output = Self> + Rem<Output = Self> + Hash + Ord + PartialOrd + Eq + PartialEq {
|
||||
const ZERO: Self;
|
||||
const MIN: Self;
|
||||
const MAX: Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_num {
|
||||
($($n:ty)*) => ($(
|
||||
impl RangeItem for $n {
|
||||
const ZERO: $n = 0;
|
||||
|
||||
const MIN: $n = <$n>::MIN;
|
||||
|
||||
const MAX: $n = <$n>::MAX;
|
||||
}
|
||||
)*)
|
||||
}
|
||||
@@ -194,8 +195,13 @@ impl_num! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 }
|
||||
|
||||
impl RangeItem for OrderedFloat<f32> {
|
||||
const ZERO: Self = OrderedFloat(0_f32);
|
||||
const MIN: Self = OrderedFloat(f32::MIN);
|
||||
const MAX: Self = OrderedFloat(f32::MAX);
|
||||
}
|
||||
|
||||
impl RangeItem for OrderedFloat<f64> {
|
||||
const ZERO: Self = OrderedFloat(0_f64);
|
||||
}
|
||||
|
||||
const MIN: Self = OrderedFloat(f64::MIN);
|
||||
const MAX: Self = OrderedFloat(f64::MAX);
|
||||
}
|
||||
|
||||
+55
-56
@@ -1,11 +1,11 @@
|
||||
use std::cell::Cell;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use flume::{Receiver, Sender, TryRecvError};
|
||||
use typed_builder::TypedBuilder;
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_buffer::FrameBuffer;
|
||||
use crate::types::CameraFormat;
|
||||
use flume::{Receiver, Sender, TryRecvError};
|
||||
use std::cell::Cell;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
/// What receiving behaviour the stream should observe.
|
||||
///
|
||||
@@ -79,23 +79,23 @@ pub enum Event {
|
||||
/// The stream is closed.
|
||||
Closed,
|
||||
/// An error from the driver
|
||||
Error(Box<dyn std::error::Error>),
|
||||
Error(String),
|
||||
/// Some other message sent by the driver. This can be ignored, although logging this is preferable.
|
||||
Other(String)
|
||||
Other(String),
|
||||
}
|
||||
|
||||
/// Represents a handle to a currently open stream.
|
||||
///
|
||||
///
|
||||
/// Streams are only valid as long as the camera is live. Any Stream that is living past a camera
|
||||
/// is invalid to use. (This doesn't cause UB, it will just kindly tell you that the stream has
|
||||
/// is invalid to use. (This doesn't cause UB, it will just kindly tell you that the stream has
|
||||
/// already closed.)
|
||||
///
|
||||
/// Streams may unexpectedly close due to unforeseen consequences e.g. webcam undergoes spontaneous
|
||||
///
|
||||
/// Streams may unexpectedly close due to unforeseen consequences e.g. webcam undergoes spontaneous
|
||||
/// deconstruction.
|
||||
///
|
||||
///
|
||||
/// The async methods [`StreamHandle::poll_event`] and [`StreamHandle::poll_frame`] **do not** respect the [`StreamReceiverBehaviour`] setting.
|
||||
///
|
||||
/// You may also close the stream from the handle side using
|
||||
///
|
||||
/// You may also close the stream from the handle side using
|
||||
#[derive(Debug)]
|
||||
pub struct StreamHandle {
|
||||
frame: Receiver<Event>,
|
||||
@@ -106,7 +106,12 @@ pub struct StreamHandle {
|
||||
|
||||
impl StreamHandle {
|
||||
/// You shouldn't be here.
|
||||
pub fn new(recv: Receiver<Event>, control: Arc<Sender<()>>, configuration: StreamConfiguration, format: CameraFormat) -> Self {
|
||||
#[must_use] pub fn new(
|
||||
recv: Receiver<Event>,
|
||||
control: Arc<Sender<()>>,
|
||||
configuration: StreamConfiguration,
|
||||
format: CameraFormat,
|
||||
) -> Self {
|
||||
Self {
|
||||
frame: recv,
|
||||
control,
|
||||
@@ -114,38 +119,35 @@ impl StreamHandle {
|
||||
format: Cell::new(format),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn configuration(&self) -> &StreamConfiguration {
|
||||
&self.configuration
|
||||
}
|
||||
|
||||
|
||||
pub fn format(&self) -> CameraFormat {
|
||||
self.format.get()
|
||||
}
|
||||
|
||||
|
||||
pub fn next_event(&self) -> Result<Event, NokhwaError> {
|
||||
let event = match self.configuration.receiver {
|
||||
StreamReceiverBehaviour::Blocking => {
|
||||
self.frame.recv().map_or_else(|_| { Event::Closed }, |e| { e })
|
||||
}
|
||||
StreamReceiverBehaviour::Timeout(time) => {
|
||||
self.frame.recv_timeout(time).map_or_else(|_| { Event::NotReady }, |e| { e })
|
||||
}
|
||||
StreamReceiverBehaviour::Try => {
|
||||
self.frame.try_recv().map_or_else(|why| {
|
||||
match why {
|
||||
TryRecvError::Empty => Event::NotReady,
|
||||
TryRecvError::Disconnected => Event::Closed,
|
||||
}
|
||||
}, |e| { e })
|
||||
self.frame.recv().unwrap_or_else(|_| Event::Closed)
|
||||
}
|
||||
StreamReceiverBehaviour::Timeout(time) => self
|
||||
.frame
|
||||
.recv_timeout(time).unwrap_or_else(|_| Event::NotReady),
|
||||
StreamReceiverBehaviour::Try => self.frame.try_recv().unwrap_or_else(
|
||||
|why| match why {
|
||||
TryRecvError::Empty => Event::NotReady,
|
||||
TryRecvError::Disconnected => Event::Closed,
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
if let Event::FormatChange(fmt) = event {
|
||||
self.format.set(fmt);
|
||||
}
|
||||
|
||||
return Ok(event)
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub fn next_frame(&self) -> Result<FrameBuffer, NokhwaError> {
|
||||
@@ -153,30 +155,30 @@ impl StreamHandle {
|
||||
let event = self.next_event()?;
|
||||
match event {
|
||||
Event::NewFrame(f) => return Ok(f),
|
||||
Event::FormatChange(_) | Event::NotReady => continue,
|
||||
Event::Terminating | Event::Closed => {
|
||||
let _ = self.control.try_send(());
|
||||
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()))
|
||||
}
|
||||
Event::Other(why) => {
|
||||
match self.configuration.on_other {
|
||||
ControlFlowOnOther::Continue => continue,
|
||||
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why))
|
||||
}
|
||||
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()));
|
||||
}
|
||||
Event::Other(why) => if self.configuration.on_other == ControlFlowOnOther::Break { return Err(NokhwaError::ReadFrameError(why)) },
|
||||
Event::Error(e) => return Err(NokhwaError::ReadFrameError(e.to_string())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub async fn poll_event(&self) -> Result<Event, NokhwaError> {
|
||||
Ok(self.frame.recv_async().await.map_or_else(|_| { Event::Closed }, |e| { if let Event::FormatChange(fmt) = e {
|
||||
self.format.set(fmt);
|
||||
}
|
||||
e
|
||||
}))
|
||||
Ok(self.frame.recv_async().await.map_or_else(
|
||||
|_| Event::Closed,
|
||||
|e| {
|
||||
if let Event::FormatChange(fmt) = e {
|
||||
self.format.set(fmt);
|
||||
}
|
||||
e
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
// TODO: a smarter implementation? maybe?
|
||||
#[cfg(feature = "async")]
|
||||
pub async fn poll_next_frame(&self) -> Result<FrameBuffer, NokhwaError> {
|
||||
@@ -184,17 +186,15 @@ impl StreamHandle {
|
||||
let event = self.poll_event().await?;
|
||||
match event {
|
||||
Event::NewFrame(f) => return Ok(f),
|
||||
Event::FormatChange(_) | Event::NotReady => continue,
|
||||
Event::Terminating | Event::Closed => {
|
||||
let _ = self.control.try_send(());
|
||||
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()))
|
||||
}
|
||||
Event::Other(why) => {
|
||||
match self.configuration.on_other {
|
||||
ControlFlowOnOther::Continue => continue,
|
||||
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why))
|
||||
}
|
||||
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()));
|
||||
}
|
||||
Event::Other(why) => match self.configuration.on_other {
|
||||
ControlFlowOnOther::Continue => continue,
|
||||
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why)),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,4 +205,3 @@ impl Drop for StreamHandle {
|
||||
let _ = self.control.try_send(());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-29
@@ -1,23 +1,23 @@
|
||||
use crate::ranges::RangeItem;
|
||||
use crate::utils::Distance;
|
||||
use crate::{error::NokhwaError, frame_format::FrameFormat};
|
||||
use num_rational::Rational32;
|
||||
use num_traits::FromPrimitive;
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::num::NonZeroI32;
|
||||
use std::ops::{Div, Rem};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
hash::{Hash},
|
||||
ops::{Sub},
|
||||
hash::Hash,
|
||||
ops::Sub,
|
||||
};
|
||||
use std::num::NonZeroI32;
|
||||
use std::ops::{Div, Rem};
|
||||
use num_rational::{Ratio, Rational32};
|
||||
use crate::ranges::{RangeItem};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
/// Describes the index of the camera.
|
||||
/// - Index: A numbered index
|
||||
/// - String: A string, used for `IPCameras` or on the Browser as DeviceIDs.
|
||||
/// - String: A string, used for `IPCameras` or on the Browser as `DeviceIDs`.
|
||||
#[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub enum CameraIndex {
|
||||
@@ -214,6 +214,8 @@ impl Rem for Resolution {
|
||||
|
||||
impl RangeItem for Resolution {
|
||||
const ZERO: Self = Resolution::new(0, 0);
|
||||
const MIN: Self = Resolution::new(u32::MIN, u32::MIN);
|
||||
const MAX: Self = Resolution::new(u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
/// Framerate of a camera, backed by a num-rational Ratio type.
|
||||
@@ -230,34 +232,34 @@ pub struct FrameRate {
|
||||
}
|
||||
|
||||
impl FrameRate {
|
||||
pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
|
||||
#[must_use] pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
|
||||
Self {
|
||||
rational: Rational32::new_raw(numerator, denominator.get()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn frame_rate(fps: i32) -> Self {
|
||||
#[must_use] pub const fn from_fps(fps: i32) -> Self {
|
||||
Self {
|
||||
rational: Rational32::new_raw(fps, 1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn numerator(&self) -> &i32 {
|
||||
self.rational.numer()
|
||||
#[must_use] pub fn numerator(&self) -> i32 {
|
||||
*self.rational.numer()
|
||||
}
|
||||
|
||||
pub fn denominator(&self) -> &i32 {
|
||||
self.rational.denom()
|
||||
#[must_use] pub fn denominator(&self) -> i32 {
|
||||
*self.rational.denom()
|
||||
}
|
||||
|
||||
pub fn as_raw(&self) -> &Rational32 {
|
||||
#[must_use] pub fn as_raw(&self) -> &Rational32 {
|
||||
&self.rational
|
||||
}
|
||||
|
||||
pub fn approximate_float(&self) -> Option<f32> {
|
||||
let numerator_float = f32::from_i32(*self.numerator())?;
|
||||
let denominator_float = f32::from_i32(*self.denominator())?;
|
||||
|
||||
#[must_use] pub fn approximate_float(&self) -> Option<f32> {
|
||||
let numerator_float = f32::from_i32(self.numerator())?;
|
||||
let denominator_float = f32::from_i32(self.denominator())?;
|
||||
|
||||
Some(numerator_float / denominator_float)
|
||||
}
|
||||
}
|
||||
@@ -307,20 +309,20 @@ impl Rem for FrameRate {
|
||||
}
|
||||
|
||||
impl RangeItem for FrameRate {
|
||||
const ZERO: Self = FrameRate::frame_rate(0);
|
||||
const ZERO: Self = FrameRate::from_fps(0);
|
||||
const MIN: Self = FrameRate::from_fps(0);
|
||||
const MAX: Self = FrameRate::from_fps(i32::MAX);
|
||||
}
|
||||
|
||||
impl From<Rational32> for FrameRate {
|
||||
fn from(value: Rational32) -> Self {
|
||||
FrameRate {
|
||||
rational: value,
|
||||
}
|
||||
FrameRate { rational: value }
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`].
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd, Eq, Ord)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct CameraFormat {
|
||||
resolution: Resolution,
|
||||
@@ -402,7 +404,7 @@ impl Default for CameraFormat {
|
||||
fn default() -> Self {
|
||||
CameraFormat {
|
||||
resolution: Resolution::new(640, 480),
|
||||
format: FrameFormat::MJpeg,
|
||||
format: FrameFormat::MJPEG,
|
||||
frame_rate: FrameRate::default(),
|
||||
}
|
||||
}
|
||||
@@ -432,8 +434,6 @@ pub struct CameraInformation {
|
||||
|
||||
impl CameraInformation {
|
||||
/// Create a new [`CameraInformation`].
|
||||
/// # JS-WASM
|
||||
/// This is exported as a constructor for [`CameraInformation`].
|
||||
#[must_use]
|
||||
// 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!
|
||||
@@ -448,8 +448,6 @@ impl CameraInformation {
|
||||
}
|
||||
|
||||
/// Get a reference to the device info's human readable name.
|
||||
/// # JS-WASM
|
||||
/// This is exported as a `get_HumanReadableName`.
|
||||
#[must_use]
|
||||
// yes, i know, unnecessary alloc this, unnecessary alloc that
|
||||
// but wasm bindgen
|
||||
|
||||
Reference in New Issue
Block a user