mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
yap yap yap
This commit is contained in:
@@ -87,6 +87,7 @@
|
||||
gmp
|
||||
openapv
|
||||
svt-av1
|
||||
mkvtoolnix-cli
|
||||
]);
|
||||
|
||||
env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library";
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
// TODO: todo, probably 0.12
|
||||
+521
-440
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
// TODO
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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:?}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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>;
|
||||
}
|
||||
|
||||
+31
-29
@@ -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 }
|
||||
}
|
||||
}
|
||||
@@ -430,7 +426,7 @@ impl Display for CameraFormat {
|
||||
|
||||
/// Information about a Camera e.g. its name.
|
||||
/// `description` and `misc` may contain information that may differ from backend to backend. Refer to each backend for details.
|
||||
/// `stable_id` contains the stable ID that may be used to reopen the same device.
|
||||
/// `stable_id` contains the stable ID that may be used to reopen the same device.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct CameraInformation {
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user