untested ffmpeg decoder, mostly finished core

This commit is contained in:
l1npengtul
2025-06-20 17:32:06 +09:00
parent 361e26e453
commit a2f458c331
27 changed files with 1880 additions and 671 deletions
+4 -7
View File
@@ -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
+26 -8
View File
@@ -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>;
}
+69
View File
@@ -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
View File
@@ -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
View File
@@ -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 {}
+5 -3
View File
@@ -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 {}
+60 -55
View File
@@ -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,
};
+21 -21
View File
@@ -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
View File
@@ -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) => {
+78
View File
@@ -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 ],
}
+8 -5
View File
@@ -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;
+8 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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