yap yap yap

This commit is contained in:
l1npengtul
2026-04-13 18:48:29 +09:00
parent ab2bedfbf1
commit 0ceddb5a85
27 changed files with 1015 additions and 1109 deletions
+1
View File
@@ -87,6 +87,7 @@
gmp
openapv
svt-av1
mkvtoolnix-cli
]);
env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library";
-8
View File
@@ -9,18 +9,10 @@ keywords = ["v4l", "v4l2", "linux", "nokhwa", "webcam"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
v4l2 = ["v4l", "v4l2-sys-mit"]
pw = ["pipewire"]
async = ["flume/async", "nokhwa-core/async"]
[dependencies]
v4l = { version = "0.14", features = ["v4l2"], optional = true }
v4l2-sys-mit = { version = "0.3", optional = true }
flume = "0.11.1"
[dependencies.pipewire]
version = "0.8"
optional = true
[dependencies.nokhwa-core]
version = "0.2"
-3
View File
@@ -13,7 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pub mod pipewire;
#[cfg(feature = "v4l2")]
pub mod v4l2;
mod v4l2r;
-1
View File
@@ -1 +0,0 @@
// TODO: todo, probably 0.12
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
// TODO
+115 -82
View File
@@ -1245,110 +1245,143 @@ pub mod wmf {
#[allow(clippy::needless_pass_by_value)]
#[allow(clippy::must_use_candidate)]
pub mod wmf {
use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation};
use std::borrow::Cow;
use nokhwa_core::{
camera::{Camera, Capture, Setting},
platform::PlatformTrait,
types::Backends,
};
pub fn initialize_mf() -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not on windows".to_string(),
))
}
pub struct MSMFCamera {}
pub fn de_initialize_mf() -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not on windows".to_string(),
))
}
pub fn query_msmf() -> Result<Vec<CameraInformation>, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not on windows".to_string(),
))
}
pub struct MediaFoundationDevice {
camera: CameraIndex,
}
impl MediaFoundationDevice {
pub fn new(_index: CameraIndex) -> Result<Self, NokhwaError> {
Ok(MediaFoundationDevice {
camera: CameraIndex::Index(0),
})
impl Setting for MSMFCamera {
fn enumerate_formats(
&self,
) -> Result<Vec<nokhwa_core::types::CameraFormat>, nokhwa_core::error::NokhwaError>
{
todo!()
}
pub fn index(&self) -> &CameraIndex {
&self.camera
fn enumerate_resolution_and_frame_rates(
&self,
frame_format: nokhwa_core::frame_format::FrameFormat,
) -> Result<
std::collections::HashMap<
nokhwa_core::types::Resolution,
Vec<nokhwa_core::types::FrameRate>,
>,
nokhwa_core::error::NokhwaError,
> {
todo!()
}
pub fn name(&self) -> String {
String::new()
}
pub fn symlink(&self) -> String {
String::new()
}
pub fn compatible_format_list(&mut self) -> Result<Vec<CameraFormat>, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
}
pub fn control(&self, _control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
}
pub fn set_control(
fn set_format(
&mut self,
_control: KnownCameraControl,
_value: ControlValue,
) -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
camera_format: nokhwa_core::types::CameraFormat,
) -> Result<(), nokhwa_core::error::NokhwaError> {
todo!()
}
pub fn format_refreshed(&mut self) -> Result<CameraFormat, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
fn control_ids(
&self,
) -> std::collections::hash_map::Keys<
'_,
nokhwa_core::control::ControlId,
nokhwa_core::control::ControlDescription,
> {
todo!()
}
pub fn format(&self) -> CameraFormat {
CameraFormat::default()
fn control_descriptions(
&self,
) -> std::collections::hash_map::Values<
'_,
nokhwa_core::control::ControlId,
nokhwa_core::control::ControlDescription,
> {
todo!()
}
pub fn set_format(&mut self, _format: CameraFormat) -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
fn control_values(
&self,
) -> std::collections::hash_map::Values<
'_,
nokhwa_core::control::ControlId,
nokhwa_core::control::ControlValue,
> {
todo!()
}
pub fn is_stream_open(&self) -> bool {
false
fn control_value(
&self,
id: &nokhwa_core::control::ControlId,
) -> Option<&nokhwa_core::control::ControlValue> {
todo!()
}
pub fn start_stream(&mut self) -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
fn control_description(
&self,
id: &nokhwa_core::control::ControlId,
) -> Option<&nokhwa_core::control::ControlDescription> {
todo!()
}
pub fn raw_bytes(&mut self) -> Result<Cow<[u8]>, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(),
))
fn set_control(
&mut self,
property: &nokhwa_core::control::ControlId,
value: nokhwa_core::control::ControlValue,
) -> Result<(), nokhwa_core::error::NokhwaError> {
todo!()
}
pub fn stop_stream(&mut self) {}
fn refresh_controls(&mut self) -> Result<(), nokhwa_core::error::NokhwaError> {
todo!()
}
}
impl Drop for MediaFoundationDevice {
fn drop(&mut self) {}
impl Capture for MSMFCamera {
fn open_stream(
&mut self,
stream_configuration: Option<nokhwa_core::stream::StreamConfiguration>,
) -> Result<
std::sync::Arc<nokhwa_core::stream::StreamHandle<'_>>,
nokhwa_core::error::NokhwaError,
> {
todo!()
}
fn close_stream(&mut self) -> Result<(), nokhwa_core::error::NokhwaError> {
todo!()
}
}
impl Camera for MSMFCamera {}
pub struct MSMFPlatform {}
impl PlatformTrait for MSMFPlatform {
const PLATFORM: nokhwa_core::types::Backends = Backends::MicrosoftMediaFoundation;
type Camera = MSMFCamera;
fn block_on_permission(&mut self) -> nokhwa_core::error::NokhwaResult<()> {
todo!()
}
fn check_permission_given(&mut self) -> bool {
todo!()
}
fn query(
&mut self,
) -> nokhwa_core::error::NokhwaResult<Vec<nokhwa_core::types::QueriedCamera>> {
todo!()
}
fn open(
&mut self,
index: nokhwa_core::types::CameraIndex,
) -> nokhwa_core::error::NokhwaResult<Self::Camera> {
todo!()
}
}
}
+8 -5
View File
@@ -12,7 +12,7 @@ repository = "https://github.com/l1npengtul/nokhwa"
[features]
default = []
serialize = ["serde", "ordered-float/serde"]
serialize = ["serde", "ordered-float/serde", "uuid/serde"]
wgpu = ["wgpu-types"]
opencv-mat = ["opencv", "opencv/clang-runtime"]
docs-features = ["serialize", "wgpu-types"]
@@ -22,14 +22,17 @@ test-fail-warnings = []
[dependencies]
thiserror = "2.0"
flume = "0.11"
flume = "0.12"
num-traits = "0.2"
ordered-float = "5"
typed-builder = "0.21"
compact_str = "0.9"
typed-builder = "0.23"
bytemuck = "1.23"
smallmap = "1.4"
paste = "1.0"
uuid = "1.19"
[dependencies.time]
version = "0.3"
default-features = false
[dependencies.num-rational]
version = "0.4"
+54 -78
View File
@@ -1,94 +1,70 @@
use crate::control::{ControlDescription, ControlId, ControlValue};
use crate::control::{Control, 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::HashMap;
use std::collections::hash_map::{Keys, Values};
use std::sync::Arc;
use crate::frame_buffer::FrameBuffer;
use crate::stream::StreamTrait;
use crate::types::CameraFormat;
pub trait CameraTrait {
type Stream: StreamTrait;
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>;
fn controls(&self) -> Result<Vec<Control>, NokhwaError>;
/// # Errors
/// Will error on
fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
fn control_value(&self, id: ControlId) -> Result<ControlValue, NokhwaError>;
fn control_ids(&self) -> Keys<'_, ControlId, ControlDescription>;
fn set_control(&self, id: ControlId, value: ControlValue) -> Result<(), NokhwaError>;
fn control_descriptions(&self) -> Values<'_, ControlId, ControlDescription>;
// fn open_stream<FrameCallback, ErrorCallback>(
// &mut self,
// camera_format: CameraFormat,
// frame_callback: FrameCallback,
// error_callback: ErrorCallback,
// ) -> Result<Self::Stream, NokhwaError>
// where
// FrameCallback: FnMut(FrameBuffer<'_>) + Send + 'static,
// ErrorCallback: FnMut(StreamEvent) + Send + 'static;
fn control_values(&self) -> Values<'_, ControlId, ControlValue>;
fn control_value(&self, id: &ControlId) -> Option<&ControlValue>;
fn control_description(&self, id: &ControlId) -> Option<&ControlDescription>;
/// # Errors
/// Will error on
fn set_control(&mut self, property: &ControlId, value: ControlValue)
-> Result<(), NokhwaError>;
/// # Errors
/// Will error on
fn refresh_controls(&mut self) -> Result<(), NokhwaError>;
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncSetting {
async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
async fn enumerate_resolution_and_frame_rates_async(
&self,
frame_format: FrameFormat,
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn set_property_async(
fn open_stream<FrameCallback, ErrorCallback>(
&mut self,
property: &ControlId,
value: ControlValue,
) -> Result<(), NokhwaError>;
camera_format: CameraFormat,
frame_callback: FrameCallback,
error_callback: ErrorCallback,
) -> Result<Self::Stream, NokhwaError>
where
FrameCallback: FnMut(FrameBuffer<'_>) + Send + 'static,
ErrorCallback: FnMut(NokhwaError) + Send + 'static;
}
pub trait Capture {
/// Implementations MUST guarantee that there can only ever be one stream open at once.
/// # Errors
/// Errors are driver specific
fn open_stream(
&mut self,
stream_configuration: Option<StreamConfiguration>,
) -> Result<Arc<StreamHandle<'_>>, NokhwaError>;
// #[cfg(feature = "async")]
// #[cfg_attr(feature = "async", async_trait::async_trait)]
// pub trait AsyncSetting {
// async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
// Implementations MUST be multi-close tolerant.
/// # Errors
/// Errors are driver specific
fn close_stream(&mut self) -> Result<(), NokhwaError>;
}
// async fn enumerate_resolution_and_frame_rates_async(
// &self,
// frame_format: FrameFormat,
// ) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncStream {
async fn open_stream_async<'a>(
&mut self,
stream_configuration: Option<StreamConfiguration>,
) -> Result<StreamHandle<'a>, NokhwaError>;
// async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
}
// async fn set_property_async(
// &mut self,
// property: &ControlId,
// value: ControlValue,
// ) -> Result<(), NokhwaError>;
// }
pub trait Camera: Setting + Capture {}
// #[cfg(feature = "async")]
// #[cfg_attr(feature = "async", async_trait::async_trait)]
// pub trait AsyncStream {
// async fn open_stream_async<'a>(
// &mut self,
// stream_configuration: Option<StreamConfiguration>,
// ) -> Result<StreamHandle<'a>, NokhwaError>;
#[cfg(feature = "async")]
pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {}
// async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
// }
// // #[cfg(feature = "async")]
// // pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {}
+129 -102
View File
@@ -1,13 +1,34 @@
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};
use std::fmt::{Display, Formatter};
use std::hash::Hash;
pub use uuid::Uuid;
pub type PlatformSpecificControlId = u64;
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum CustomControlId {
U32(u32),
U64(u64),
Uuid(Uuid),
}
impl From<u32> for CustomControlId {
fn from(value: u32) -> Self {
CustomControlId::U32(value)
}
}
impl From<u64> for CustomControlId {
fn from(value: u64) -> Self {
CustomControlId::U64(value)
}
}
impl From<Uuid> for CustomControlId {
fn from(value: Uuid) -> Self {
CustomControlId::Uuid(value)
}
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub enum ControlId {
@@ -44,7 +65,7 @@ pub enum ControlId {
Orientation,
PlatformSpecific(PlatformSpecificControlId),
Custom(CustomControlId),
}
impl Display for ControlId {
@@ -53,115 +74,115 @@ impl Display for ControlId {
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Controls {
descriptions: HashMap<ControlId, ControlDescription>,
values: HashMap<ControlId, ControlValue>,
}
// #[derive(Clone, Debug, Default, PartialEq)]
// pub struct Controls {
// descriptions: HashMap<ControlId, ControlDescription>,
// values: HashMap<ControlId, ControlValue>,
// }
impl Controls {
/// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls`
#[must_use]
pub fn new(
device_controls: HashMap<ControlId, ControlDescription>,
device_values: HashMap<ControlId, ControlValue>,
) -> Option<Self> {
for (id, value) in &device_values {
if let Some(description) = device_controls.get(id)
&& !description.validate(value)
{
return None;
}
}
// impl Controls {
// /// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls`
// #[must_use]
// pub fn new(
// device_controls: HashMap<ControlId, ControlDescription>,
// device_values: HashMap<ControlId, ControlValue>,
// ) -> Option<Self> {
// for (id, value) in &device_values {
// if let Some(description) = device_controls.get(id)
// && !description.validate(value)
// {
// return None;
// }
// }
Some(Self {
descriptions: device_controls,
values: device_values,
})
}
// Some(Self {
// descriptions: device_controls,
// values: device_values,
// })
// }
#[must_use]
pub fn empty() -> Self {
Self::default()
}
// #[must_use]
// pub fn empty() -> Self {
// Self::default()
// }
#[must_use]
pub fn unchecked_new(
device_controls: HashMap<ControlId, ControlDescription>,
device_values: HashMap<ControlId, ControlValue>,
) -> Self {
Self {
descriptions: device_controls,
values: device_values,
}
}
// #[must_use]
// pub fn unchecked_new(
// device_controls: HashMap<ControlId, ControlDescription>,
// device_values: HashMap<ControlId, ControlValue>,
// ) -> Self {
// Self {
// descriptions: device_controls,
// values: device_values,
// }
// }
#[must_use]
pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
self.descriptions.get(control_id)
}
// #[must_use]
// pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
// self.descriptions.get(control_id)
// }
#[must_use]
pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
self.values.get(control_id)
}
// #[must_use]
// pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
// self.values.get(control_id)
// }
#[must_use]
pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> {
self.descriptions.values()
}
// #[must_use]
// pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> {
// self.descriptions.values()
// }
#[must_use]
pub fn values(&self) -> Values<'_, ControlId, ControlValue> {
self.values.values()
}
// #[must_use]
// pub fn values(&self) -> Values<'_, ControlId, ControlValue> {
// self.values.values()
// }
#[must_use]
pub fn ids(&self) -> Keys<'_, ControlId, ControlDescription> {
self.descriptions.keys()
}
// #[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 Some(description) = self.descriptions.get(control_id) else {
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 !self.values.contains_key(control_id) {
return Err(NokhwaError::GetPropertyError {
property: control_id.to_string(),
error: "ID Not Found".to_string(),
});
}
// if !self.values.contains_key(control_id) {
// return Err(NokhwaError::GetPropertyError {
// property: control_id.to_string(),
// error: "ID Not Found".to_string(),
// });
// }
Ok(description.validate(value))
}
// Ok(description.validate(value))
// }
pub fn set_control_value(
&mut self,
control_id: &ControlId,
value: ControlValue,
) -> NokhwaResult<()> {
match self.values.get_mut(control_id) {
Some(old) => {
*old = value;
Ok(())
}
// this should not happen,
None => Err(NokhwaError::SetPropertyError {
property: control_id.to_string(),
value: value.to_string(),
error: "ID Not Found".to_string(),
}),
}
}
}
// pub fn set_control_value(
// &mut self,
// control_id: &ControlId,
// value: ControlValue,
// ) -> NokhwaResult<()> {
// match self.values.get_mut(control_id) {
// Some(old) => {
// *old = value;
// Ok(())
// }
// // this should not happen,
// None => Err(NokhwaError::SetPropertyError {
// property: control_id.to_string(),
// value: value.to_string(),
// error: "ID Not Found".to_string(),
// }),
// }
// }
// }
#[derive(Clone, Debug, PartialEq)]
pub struct ControlDescription {
@@ -347,7 +368,7 @@ pub enum ControlValue {
Integer(i64),
BitMask(u64),
Float(OrderedFloat<f64>),
String(CompactString),
String(String),
Boolean(bool),
Array(Vec<ControlValue>),
Binary(Vec<u8>),
@@ -480,3 +501,9 @@ impl Display for Orientation {
write!(f, "Orientation {self:?}")
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Control {
pub id: ControlId,
pub description: ControlDescription,
}
+58 -47
View File
@@ -1,3 +1,4 @@
use crate::control::{ControlId, ControlValue};
/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
*
@@ -15,8 +16,9 @@
*/
use crate::frame_format::{CustomFrameFormat, FrameFormat};
use crate::pixel_destination::PixelDestination;
use crate::types::Backends;
use std::fmt::Debug;
use crate::types::{Backends, CameraIndex};
use std::fmt::{Debug, Display};
use std::num::ParseIntError;
use thiserror::Error;
pub type NokhwaResult<T> = Result<T, NokhwaError>;
@@ -25,44 +27,59 @@ pub type NokhwaResult<T> = Result<T, NokhwaError>;
#[allow(clippy::module_name_repetitions)]
#[derive(Error, Debug, Clone)]
pub enum NokhwaError {
// NokhwaCore Errors
#[error("Failed to parse string index to u32: {0}")]
IndexParsingFailed(ParseIntError),
// Platform Errors
#[error("Could not initialize {backend}: {error}")]
InitializeError { backend: Backends, error: String },
#[error("Could not shutdown {backend}: {error}")]
ShutdownError { backend: Backends, error: String },
#[error("Error: {0}")]
GeneralError(String),
#[error("Could not generate required structure {structure}: {error}")]
StructureError { structure: String, error: String },
#[error("Could not open device {0}: {1}")]
OpenDeviceError(String, String),
#[error("Could not get device property {property}: {error}")]
GetPropertyError { property: String, error: String },
#[error("Could not set device property {property} with value {value}: {error}")]
SetPropertyError {
property: String,
value: String,
OpenDeviceError(CameraIndex, String),
#[error("Failed to query for cameras: {0}")]
QueryError(String),
// Camera Errors
#[error("Failed to list FrameFormats: {0}")]
ListFourCCError(String),
#[error("Failed to list Resolutions: {0}")]
ListResolutionError(String),
#[error("Failed to list FrameRates: {0}")]
ListFrameRatesError(String),
#[error("Failed to list Controls: {0}")]
ListControlError(String),
#[error("{0:?} is an invalid control id: {1}")]
InvalidControlId(ControlId, String),
#[error("{0:?} is an invalid control value.")]
InvalidControlValue(ControlValue),
#[error("{0:?} is an invalid frame format: {1}")]
InvalidFrameFormat(FrameFormat, String),
#[error("Failed to get control descriptor for {0}: {1}")]
FailedToGetControlDescriptor(ControlId, String),
#[error("Could not shutdown {backend} device {device}: {error}")]
ShutdownError {
backend: Backends,
device: CameraIndex,
error: String,
},
// Stream Related Errors
#[error("Could not open device stream: {0}")]
OpenStreamError(String),
#[error("Could not capture frame: {0}")]
ReadFrameError(String),
#[error("Could not process frame {src} to {destination}: {error}")]
ProcessFrameError {
src: FrameFormat,
destination: String,
error: String,
},
#[error("Error occured during stream: {0:?}")]
StreamError(StreamError),
#[error("Could not stop stream: {0}")]
StreamShutdownError(String),
#[error("Device no longer exists: {0}")]
DeviceNoLongerExists(CameraIndex),
// Not-Implemented Errors
#[error("This operation is not supported by backend {0}.")]
UnsupportedOperationError(Backends),
#[error("This operation is not implemented yet: {0}")]
NotImplementedError(String),
#[error("Failed To Convert: {0}")]
ConversionError(String),
#[error("Permission denied by user.")]
PermissionDenied,
// Decoders
#[error("Failed to decode: {0}")]
Decoder(String),
#[error("Unsupported FrameFormat: {0}")]
@@ -98,23 +115,17 @@ impl NokhwaError {
}
}
//
// pub enum InitializeError {}
//
// pub enum QueryBackendError {}
//
// pub enum OpenDeviceError {}
//
// pub enum QueryDeviceError {}
//
// pub enum GetPropertyError {}
//
// pub enum SetPropertyError {}
//
// pub enum OpenStreamError {}
//
// pub enum CloseStreamError {}
//
// pub enum FrameError {}
//
// pub enum DecoderError {}
/// Errors that may occur during a stream
#[derive(Error, Debug, Clone)]
pub enum StreamError {
NotReady,
NoLongerExists,
StreamInvalidated,
Other(String),
}
impl Display for StreamError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
+2 -62
View File
@@ -13,75 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::control::ControlValue;
pub use compact_str::CompactString;
pub use smallmap::Map;
use crate::metadata::Metadata;
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
pub type PlatformSpecificFlag = u32;
#[derive(Clone, Debug, Default)]
pub struct Metadata {
flags: Map<CompactString, ControlValue>,
}
impl Metadata {
#[must_use]
pub fn new() -> Self {
Self {
flags: Map::default(),
}
}
#[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);
}
}
impl Hash for Metadata {
fn hash<H: Hasher>(&self, state: &mut H) {
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.iter() {
if let Some(other_value) = other.flags.get(this_key) {
if this_value != other_value {
return false;
}
} else {
return false;
}
}
true
}
}
/// A buffer returned by a camera to accommodate custom decoding.
/// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer.
///
/// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames.
#[derive(Clone, Debug, Hash, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct FrameBuffer<'a> {
buffer: Cow<'a, [u8]>,
metadata: Option<Metadata>,
+4 -3
View File
@@ -16,6 +16,7 @@
use ordered_float::OrderedFloat;
use std::fmt::{Display, Formatter};
pub use uuid::Uuid;
macro_rules! define_frame_format_with_groups {
(
@@ -209,7 +210,7 @@ impl FrameFormat {
#[must_use]
pub fn is_custom(&self) -> bool {
if let FrameFormat::Custom(_) = self {
return true
return true;
}
false
}
@@ -224,8 +225,8 @@ impl Display for FrameFormat {
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum CustomFrameFormat {
UUID(u128),
FourCC([char; 4]),
UUID(Uuid),
FourCC([u8; 4]),
U32(u32),
U64(u64),
F32(OrderedFloat<f32>),
+2 -1
View File
@@ -29,10 +29,11 @@ pub mod format_request;
pub mod frame_buffer;
pub mod frame_format;
pub mod image;
pub mod metadata;
pub mod pixel_destination;
pub mod platform;
pub mod ranges;
pub mod stream;
pub mod traits;
pub mod types;
pub mod utils;
pub mod pixel_destination;
+58
View File
@@ -0,0 +1,58 @@
use std::{collections::HashSet, fmt::Display, hash::Hash};
pub use time::Time;
pub use uuid::Uuid;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum MetadataValue {
String(String),
Uuid(Uuid),
I64(i64),
U64(u64),
I32(i32),
U32(u32),
Byte(u8),
Timestamp(Time),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum MetadataTypes {
Size(u64),
Timestamp(Time),
PlatformBitflag32(u32),
PlatformBitflag64(u64),
Sequence(u64),
FieldOrder(u32),
Custom { name: String, value: MetadataValue },
}
impl Hash for MetadataTypes {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
MetadataTypes::Custom { name, .. } => name.hash(state),
meta => core::mem::discriminant(meta).hash(state),
}
}
}
impl Display for MetadataTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MetadataTypes::Size(s) => write!(f, "Metadata Size: {s}b"),
MetadataTypes::Timestamp(time) => {
let (hour, minute, second, micro) = time.as_hms_micro();
write!(
f,
"Metadata Timestamp: {hour}h {minute}m {second}s {micro}us"
)
}
MetadataTypes::PlatformBitflag32(bf) => write!(f, "Metadata Bitflag 32bit: {bf:032b}"),
MetadataTypes::PlatformBitflag64(bf) => write!(f, "Metadata Bitflag 32bit: {bf:064b}"),
MetadataTypes::Sequence(seq) => write!(f, "Metadata Order in Sequence: #{seq}"),
MetadataTypes::FieldOrder(or) => write!(f, "Metadata Field Order: {or}"),
MetadataTypes::Custom { name, value } => write!(f, "Metadata {name}: {value:?}"),
}
}
}
pub type Metadata = HashSet<MetadataTypes>;
+19 -27
View File
@@ -1,11 +1,10 @@
use crate::camera::Camera;
use crate::camera::CameraTrait;
use crate::error::NokhwaResult;
use crate::types::{Backends, CameraIndex, QueriedCamera};
pub trait PlatformTrait {
const PLATFORM: Backends;
type Camera: Camera;
type Camera: CameraTrait;
fn block_on_permission(&mut self) -> NokhwaResult<()>;
@@ -14,33 +13,26 @@ pub trait PlatformTrait {
fn query(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
fn open(&mut self, index: CameraIndex) -> NokhwaResult<Self::Camera>;
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")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncPlatformTrait: PlatformTrait {
const PLATFORM: Backends;
type AsyncCamera: crate::camera::AsyncCamera;
// #[cfg(feature = "async")]
// #[cfg_attr(feature = "async", async_trait::async_trait)]
// pub trait AsyncPlatformTrait: PlatformTrait {
// const PLATFORM: Backends;
// type AsyncCamera: crate::camera::AsyncCamera;
async fn await_permission(&mut self) -> NokhwaResult<()>;
// async fn await_permission(&mut self) -> NokhwaResult<()>;
async fn query_async(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
// async fn query_async(&mut self) -> NokhwaResult<Vec<QueriedCamera>>;
async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
// async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
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>)
}
}
// 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>)
// }
// }
+5 -211
View File
@@ -1,213 +1,7 @@
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;
use crate::{error::NokhwaError, types::CameraFormat};
/// What receiving behaviour the stream should observe.
///
/// Note that [`StreamHandleTrait::poll_frame`] does not respect [`StreamReceiverBehaviour::Timeout`] -
/// it will either immediately return (try once) or block until the next frame or error.
///
/// The default behaviour is to block until a new event is sent.
#[derive(Clone, Debug, Default, PartialOrd, PartialEq)]
pub enum StreamReceiverBehaviour {
/// Blocks until a new event is sent to the Stream.
#[default]
Blocking,
/// Only waits [duration] amount of time for a new event, returning [`Event::NotReady`] otherwise.
Timeout(Duration),
/// Immediately return. If there is no event waiting for the stream, it will return an [`Event::NotReady`] instead.
Try,
}
/// How many events a stream can hold. By default, it is **one**.
///
/// This means that streams will be blocked until the stream handle is emptied.
#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub enum StreamBounds {
Bounded(u32),
Unbounded,
}
impl Default for StreamBounds {
fn default() -> Self {
StreamBounds::Bounded(1)
}
}
#[derive(Clone, Default, Debug, PartialOrd, PartialEq)]
pub enum ControlFlowOnOther {
Continue,
#[default]
Break,
}
/// Configuration for a [`StreamHandle`].
#[derive(Clone, Debug, Default, PartialOrd, PartialEq, TypedBuilder)]
pub struct StreamConfiguration {
#[builder(default)]
pub receiver: StreamReceiverBehaviour,
#[builder(default)]
pub bound: StreamBounds,
#[builder(default)]
pub on_other: ControlFlowOnOther,
}
/// Possible events to receive from an active stream.
#[derive(Clone, Debug, PartialEq)]
pub enum Event<'a> {
/// A new frame.
NewFrame(FrameBuffer<'a>),
/// Camera Format Changed.
///
/// This will usually require the reset of a buffer, or be followed by a [`Event::Terminated`],
/// depending on the backend used.
FormatChange(CameraFormat),
/// This stream is not ready for another event. This is **never** sent by the stream itself, but
/// instead a [`StreamHandle`] construct for when the user sets [`StreamReceiverBehaviour`] to either
/// [`StreamReceiverBehaviour::Timeout`] or [`StreamReceiverBehaviour::Try`] but the stream does not
/// have the data ready.
///
/// (This can be ignored when iterating, or using the [`StreamReceiverBehaviour::Blocking`] approach.)
NotReady,
/// The stream will be ended shortly. Users should call [`StreamHandleTrait::close_stream`] afterwards.
Terminating,
/// The stream is closed.
Closed,
/// An error from the driver
Error(String),
/// Some other message sent by the driver. This can be ignored, although logging this is preferable.
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
/// already closed.)
///
/// 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
#[derive(Debug)]
pub struct StreamHandle<'a> {
frame: Receiver<Event<'a>>,
control: Arc<Sender<()>>,
configuration: StreamConfiguration,
format: Cell<CameraFormat>,
}
impl<'a> StreamHandle<'a> {
/// You shouldn't be here.
#[must_use]
pub fn new(
recv: Receiver<Event<'a>>,
control: Arc<Sender<()>>,
configuration: StreamConfiguration,
format: CameraFormat,
) -> Self {
Self {
frame: recv,
control,
configuration,
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().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);
}
Ok(event)
}
pub fn next_frame(&self) -> Result<FrameBuffer<'_>, NokhwaError> {
loop {
let event = self.next_event()?;
match event {
Event::NewFrame(f) => return Ok(f),
Event::Terminating | Event::Closed => {
let _ = self.control.try_send(());
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.clone())),
_ => {}
}
}
}
#[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
},
))
}
// TODO: a smarter implementation? maybe?
#[cfg(feature = "async")]
pub async fn poll_next_frame(&self) -> Result<FrameBuffer<'_>, NokhwaError> {
loop {
let event = self.poll_event().await?;
match event {
Event::NewFrame(f) => return Ok(f),
Event::Terminating | Event::Closed => {
let _ = self.control.try_send(());
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()));
}
Event::Other(why) => {
if let ControlFlowOnOther::Break = self.configuration.on_other {
return Err(NokhwaError::ReadFrameError(why));
}
}
_ => {}
}
}
}
}
impl Drop for StreamHandle<'_> {
fn drop(&mut self) {
let _ = self.control.try_send(());
}
pub trait StreamTrait {
fn current_format(&self) -> &CameraFormat;
fn stop_stream(self) -> Result<(), NokhwaError>;
}
+30 -28
View File
@@ -1,11 +1,10 @@
use crate::ranges::RangeItem;
use crate::utils::Distance;
use crate::{error::NokhwaError, frame_format::FrameFormat};
use num_rational::Rational32;
use num_rational::Ratio;
use num_traits::FromPrimitive;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::num::NonZeroI32;
use std::ops::{Div, Rem};
use std::{
cmp::Ordering,
@@ -22,7 +21,7 @@ use std::{
pub enum CameraIndex {
Index(u32),
String(String),
Stable(String)
Stable(String),
}
impl CameraIndex {
@@ -32,9 +31,9 @@ impl CameraIndex {
pub fn as_index(&self) -> Result<u32, NokhwaError> {
match self {
CameraIndex::Index(i) => Ok(*i),
CameraIndex::String(s) | CameraIndex::Stable(s) => s
.parse::<u32>()
.map_err(|why| NokhwaError::GeneralError(why.to_string())),
CameraIndex::String(s) | CameraIndex::Stable(s) => {
s.parse::<u32>().map_err(NokhwaError::IndexParsingFailed)
}
}
}
@@ -44,7 +43,7 @@ impl CameraIndex {
match self {
CameraIndex::Index(i) => i.to_string(),
CameraIndex::String(s) | CameraIndex::Stable(s) => s.clone(),
}
}
}
/// Returns true if this [`CameraIndex`] contains an [`CameraIndex::Index`]
@@ -108,10 +107,7 @@ impl Resolution {
#[must_use]
// TODO: make this height and width.
pub const fn new(width: u32, height: u32) -> Self {
Resolution {
width,
height,
}
Resolution { width, height }
}
/// Get the width of Resolution
@@ -230,43 +226,43 @@ impl RangeItem for Resolution {
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct FrameRate {
rational: Rational32,
rational: Ratio<u32>,
}
impl FrameRate {
#[must_use]
pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
pub const fn new(numerator: u32, denominator: u32) -> Self {
Self {
rational: Rational32::new_raw(numerator, denominator.get()),
rational: Ratio::<u32>::new_raw(numerator, denominator),
}
}
#[must_use]
pub const fn from_fps(fps: i32) -> Self {
pub const fn from_fps(fps: u32) -> Self {
Self {
rational: Rational32::new_raw(fps, 1),
rational: Ratio::<u32>::new_raw(fps, 1),
}
}
#[must_use]
pub fn numerator(&self) -> i32 {
pub fn numerator(&self) -> u32 {
*self.rational.numer()
}
#[must_use]
pub fn denominator(&self) -> i32 {
pub fn denominator(&self) -> u32 {
*self.rational.denom()
}
#[must_use]
pub fn as_raw(&self) -> &Rational32 {
pub fn as_raw(&self) -> &Ratio<u32> {
&self.rational
}
#[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())?;
let numerator_float = f32::from_u32(self.numerator())?;
let denominator_float = f32::from_u32(self.denominator())?;
Some(numerator_float / denominator_float)
}
@@ -274,7 +270,7 @@ impl FrameRate {
impl Default for FrameRate {
fn default() -> Self {
FrameRate::new(30, NonZeroI32::new(1).unwrap())
FrameRate::new(30, 1)
}
}
@@ -319,11 +315,11 @@ impl Rem for FrameRate {
impl RangeItem for FrameRate {
const ZERO: Self = FrameRate::from_fps(0);
const MIN: Self = FrameRate::from_fps(0);
const MAX: Self = FrameRate::from_fps(i32::MAX);
const MAX: Self = FrameRate::from_fps(u32::MAX);
}
impl From<Rational32> for FrameRate {
fn from(value: Rational32) -> Self {
impl From<Ratio<u32>> for FrameRate {
fn from(value: Ratio<u32>) -> Self {
FrameRate { rational: value }
}
}
@@ -446,7 +442,12 @@ impl CameraInformation {
// OK, i just checkeed back on this code. WTF was I on when I wrote `&(impl AsRef<str> + ?Sized)` ????
// I need to get on the same shit that my previous self was on, because holy shit that stuff is strong as FUCK!
// Finally fixed this insanity. Hopefully I didnt torment anyone by actually putting this in a stable release.
pub fn new(human_name: String, description: String, misc: String, stable_id: Option<String>) -> Self {
pub fn new(
human_name: String,
description: String,
misc: String,
stable_id: Option<String>,
) -> Self {
CameraInformation {
human_name,
description,
@@ -475,7 +476,8 @@ impl CameraInformation {
&self.misc
}
#[must_use] pub fn stable_id(&self) -> Option<&str> {
#[must_use]
pub fn stable_id(&self) -> Option<&str> {
self.stable_id.as_deref()
}
}
@@ -509,5 +511,5 @@ impl Display for Backends {
#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct QueriedCamera {
pub index: CameraIndex,
pub information: CameraInformation
pub information: CameraInformation,
}
+2 -3
View File
@@ -8,8 +8,7 @@ ffmpeg = ["ffmpeg-the-third"]
yuyv = ["dcv-color-primitives", "yuv"]
mjpeg = ["zune-jpeg", "zune-core"]
luma = ["nokhwa-iter-extensions", "itermore"]
static = ["ffmpeg-the-third/static"]
#async = []
#ffmpeg-static = ["ffmpeg-the-third/static"]
[dependencies]
bytemuck = "1.23"
@@ -50,4 +49,4 @@ optional = true
[dev-dependencies.image]
workspace = true
features = ["png", "jpeg", "avif"]
features = ["png", "jpeg"]
+6 -6
View File
@@ -54,8 +54,8 @@ fn create_video(config: &FfmpegConfig) -> Result<Video, NokhwaError> {
})?;
let frame_rate = AVRational {
num: config.frame_rate.numerator(),
den: config.frame_rate.denominator(),
num: config.frame_rate.numerator() as i32,
den: config.frame_rate.denominator() as i32,
};
let codec_i32 = unsafe { transmute::<AVCodecID, i32>(AVCodecID::from(id)) };
let intermediate_config = IntermediateDecoderConfig {
@@ -335,7 +335,7 @@ fn convert_format_to_codec_id(frame_format: &FrameFormat) -> Option<Id> {
}
match frame_format {
FrameFormat::H265 => Some(Id::H265),
FrameFormat::H265 => Some(Id::HEVC),
FrameFormat::H264 => Some(Id::H264),
FrameFormat::AVC1 => Some(Id::H264),
FrameFormat::H263 => Some(Id::H263),
@@ -652,7 +652,7 @@ const fn is_little_endian() -> bool {
#[cfg(test)]
mod test {
use ffmpeg_the_third::{codec::Context, format::input, media::Type, threading::Config};
use ffmpeg_the_third::format::input;
use image::{ImageFormat, Rgb};
use nokhwa_core::{
decoder::Decoder,
@@ -712,13 +712,13 @@ mod test {
pub fn test_h265() {
ffmpeg_the_third::init().unwrap();
let file = "test_images/ffmpeg/h265/out.h265";
let file = "test_images/ffmpeg/h265/bitstream.265";
let output_dir = "test_images/ffmpeg/h265/out";
let decoder_cfg = FfmpegConfig {
custom_frame_format_map: None,
frame_format: FrameFormat::H265,
resolution: Resolution::new(498, 348),
resolution: Resolution::new(480, 270),
frame_rate: FrameRate::from_fps(30),
parallelism: Parallelism::default(),
ffmpeg_codec_low_level: FfmpegDecoderConfig::default(),
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.