mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 10:37:26 +00:00
(untested) preliminary completion of V4L2 backend. TODO: decoder API
This commit is contained in:
@@ -20,3 +20,6 @@ devenv.local.nix
|
|||||||
|
|
||||||
# pre-commit
|
# pre-commit
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
|
|
||||||
|
# this is a library so its okgit
|
||||||
|
Cargo.lock
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ version = "0.2"
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.wgpu]
|
[dependencies.wgpu]
|
||||||
version = "24"
|
version = "25"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.opencv]
|
[dependencies.opencv]
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
use nokhwa_core::camera::{Camera, Capture, Setting};
|
use nokhwa_core::camera::{Camera, Capture, Setting};
|
||||||
use nokhwa_core::control::{ControlDescription, ControlFlags, ControlId, ControlValue, ControlValueDescriptor, Controls};
|
use nokhwa_core::control::{ControlDescription, ControlFlags, ControlId, ControlValue, ControlValueDescriptor, Controls, Orientation};
|
||||||
use nokhwa_core::error::{NokhwaError, NokhwaResult};
|
use nokhwa_core::error::{NokhwaError, NokhwaResult};
|
||||||
use nokhwa_core::frame_format::FrameFormat;
|
use nokhwa_core::frame_format::FrameFormat;
|
||||||
use nokhwa_core::platform::{Backends, PlatformTrait};
|
use nokhwa_core::platform::{Backends, PlatformTrait};
|
||||||
use nokhwa_core::ranges::Range;
|
use nokhwa_core::ranges::Range;
|
||||||
use nokhwa_core::stream::{StreamHandle, StreamConfiguration, StreamInnerTrait};
|
use nokhwa_core::stream::{Event, StreamBounds, StreamConfiguration, StreamHandle};
|
||||||
use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution};
|
use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution};
|
||||||
use std::collections::hash_map::{Keys, Values};
|
use std::collections::hash_map::{Keys, Values};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::num::NonZeroI32;
|
use std::num::NonZeroI32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::{sleep, JoinHandle};
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
use flume::{Sender, unbounded, bounded};
|
||||||
use flume::{Sender, Receiver, unbounded, bounded};
|
|
||||||
use v4l::context::enum_devices;
|
use v4l::context::enum_devices;
|
||||||
use v4l::control::{Description, Flags, MenuItem, Type, Value};
|
use v4l::control::{Description, Flags, MenuItem, Type, Value};
|
||||||
use v4l::frameinterval::FrameIntervalEnum;
|
use v4l::frameinterval::FrameIntervalEnum;
|
||||||
use v4l::video::output::Parameters;
|
use v4l::video::output::Parameters;
|
||||||
use v4l::video::Output;
|
use v4l::video::Output;
|
||||||
use v4l::{Capabilities, Device, Format, FourCC, Fraction, FrameInterval};
|
use v4l::{Capabilities, Control, Device, Format, FourCC, Fraction, FrameInterval};
|
||||||
use v4l2_sys_mit::{V4L2_CID_AUTO_EXPOSURE_BIAS, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_CID_AUTO_FOCUS_STATUS, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_CAMERA_ORIENTATION, V4L2_CID_EXPOSURE_ABSOLUTE, V4L2_CID_EXPOSURE_AUTO, V4L2_CID_EXPOSURE_METERING, V4L2_CID_FLASH_LED_MODE, V4L2_CID_FLASH_STROBE, V4L2_CID_FLASH_STROBE_STATUS, V4L2_CID_FLASH_STROBE_STOP, V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_AUTO, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_ISO_SENSITIVITY, V4L2_CID_ISO_SENSITIVITY_AUTO, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE};
|
use v4l2_sys_mit::{V4L2_CAMERA_ORIENTATION_BACK, V4L2_CAMERA_ORIENTATION_EXTERNAL, V4L2_CAMERA_ORIENTATION_FRONT, V4L2_CID_AUTO_EXPOSURE_BIAS, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_CID_AUTO_FOCUS_STATUS, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_CAMERA_ORIENTATION, V4L2_CID_EXPOSURE_ABSOLUTE, V4L2_CID_EXPOSURE_AUTO, V4L2_CID_EXPOSURE_METERING, V4L2_CID_FLASH_LED_MODE, V4L2_CID_FLASH_STROBE, V4L2_CID_FLASH_STROBE_STATUS, V4L2_CID_FLASH_STROBE_STOP, V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_AUTO, V4L2_CID_FOCUS_RELATIVE, V4L2_CID_IRIS_ABSOLUTE, V4L2_CID_IRIS_RELATIVE, V4L2_CID_ISO_SENSITIVITY, V4L2_CID_ISO_SENSITIVITY_AUTO, V4L2_CID_ZOOM_ABSOLUTE, V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE};
|
||||||
use v4l::io::traits::OutputStream;
|
use v4l::io::traits::OutputStream;
|
||||||
use v4l::prelude::MmapStream;
|
use v4l::prelude::MmapStream;
|
||||||
use nokhwa_core::frame_buffer::FrameBuffer;
|
use nokhwa_core::frame_buffer::{CompactString, FrameBuffer, Metadata};
|
||||||
|
|
||||||
fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> CameraInformation {
|
fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> CameraInformation {
|
||||||
let name = capabilities.card;
|
let name = capabilities.card;
|
||||||
@@ -249,7 +249,7 @@ fn convert_description_to_ctrl_body(description: Description) -> Option<ControlD
|
|||||||
Type::Bitmask => {
|
Type::Bitmask => {
|
||||||
(
|
(
|
||||||
ControlValueDescriptor::BitMask,
|
ControlValueDescriptor::BitMask,
|
||||||
Some(ControlValue::BitMask(description.default))
|
Some(ControlValue::BitMask(description.default as u64))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Type::IntegerMenu | Type::Menu => {
|
Type::IntegerMenu | Type::Menu => {
|
||||||
@@ -294,6 +294,34 @@ fn convert_description_to_ctrl_body(description: Description) -> Option<ControlD
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn conv_control_value_to_v4l_value(control: ControlValue) -> Result<Value, NokhwaError> {
|
||||||
|
let value = match control {
|
||||||
|
ControlValue::Null => Value::None,
|
||||||
|
ControlValue::Integer(i) | ControlValue::BitMask(i) => Value::Integer(i),
|
||||||
|
ControlValue::String(s) => Value::String(s),
|
||||||
|
ControlValue::Boolean(t) => Value::Boolean(t),
|
||||||
|
ControlValue::Binary(b) => Value::CompoundU8(b),
|
||||||
|
ControlValue::EnumPick(e) => {
|
||||||
|
if let ControlValue::Integer(i) = &e {
|
||||||
|
Value::Integer(*i)
|
||||||
|
} else {
|
||||||
|
return Err(NokhwaError::ConversionError("could not convert non integer enum pick".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ControlValue::Orientation(o) => Value::Integer(match o {
|
||||||
|
Orientation::User => V4L2_CAMERA_ORIENTATION_FRONT as i64,
|
||||||
|
Orientation::Environment => V4L2_CAMERA_ORIENTATION_BACK as i64,
|
||||||
|
Orientation::Custom(i) => i,
|
||||||
|
_ => V4L2_CAMERA_ORIENTATION_EXTERNAL as i64,
|
||||||
|
}),
|
||||||
|
_ => {
|
||||||
|
return Err(NokhwaError::ConversionError("Conversion not supported for this data type.".to_string()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct V4L2Platform {}
|
pub struct V4L2Platform {}
|
||||||
|
|
||||||
@@ -349,7 +377,7 @@ pub struct V4L2Camera {
|
|||||||
camera_format: Option<CameraFormat>,
|
camera_format: Option<CameraFormat>,
|
||||||
camera_index: CameraIndex,
|
camera_index: CameraIndex,
|
||||||
controls: Controls,
|
controls: Controls,
|
||||||
stream: Option<Arc<StreamHandle>>,
|
stream: Option<V4L2Stream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Setting for V4L2Camera {
|
impl Setting for V4L2Camera {
|
||||||
@@ -419,7 +447,7 @@ impl Setting for V4L2Camera {
|
|||||||
}).flatten().collect::<HashMap<Resolution, Vec<FrameRate>>>())
|
}).flatten().collect::<HashMap<Resolution, Vec<FrameRate>>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError> {
|
fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError> {
|
||||||
let fourcc = frame_format_to_fourcc(*camera_format.format())?;
|
let fourcc = frame_format_to_fourcc(*camera_format.format())?;
|
||||||
self.device.set_format(
|
self.device.set_format(
|
||||||
&Format::new(camera_format.width(), camera_format.height(), fourcc)
|
&Format::new(camera_format.width(), camera_format.height(), fourcc)
|
||||||
@@ -435,6 +463,7 @@ impl Setting for V4L2Camera {
|
|||||||
error: why.to_string(),
|
error: why.to_string(),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
self.camera_format = Some(camera_format);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +488,24 @@ impl Setting for V4L2Camera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_control(&mut self, property: &ControlId, value: ControlValue) -> Result<(), NokhwaError> {
|
fn set_control(&mut self, property: &ControlId, value: ControlValue) -> Result<(), NokhwaError> {
|
||||||
self.controls.set_control_value(property, value)
|
if !self.controls.validate(property, &value)? {
|
||||||
|
return Err(NokhwaError::SetPropertyError {
|
||||||
|
property: property.to_string(),
|
||||||
|
value: value.to_string(),
|
||||||
|
error: "failed to validate".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let cid = control_id_to_cid(*property)?;
|
||||||
|
let v4l_value = conv_control_value_to_v4l_value(value.clone())?;
|
||||||
|
self.device.set_control(Control { id: cid, value: v4l_value }).map_err(|why| {
|
||||||
|
Err(NokhwaError::SetPropertyError {
|
||||||
|
property: cid.to_string(),
|
||||||
|
value: value.to_string(),
|
||||||
|
error: why.to_string(),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
self.controls.set_control_value(property, value)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_controls(&mut self) -> Result<(), NokhwaError> {
|
fn refresh_controls(&mut self) -> Result<(), NokhwaError> {
|
||||||
@@ -503,8 +549,7 @@ impl Setting for V4L2Camera {
|
|||||||
|
|
||||||
struct V4L2Stream {
|
struct V4L2Stream {
|
||||||
thread: JoinHandle<()>,
|
thread: JoinHandle<()>,
|
||||||
control: Sender<()>,
|
control: Arc<Sender<()>>,
|
||||||
receiver: Arc<Receiver<NokhwaResult<FrameBuffer>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for V4L2Stream {
|
impl Drop for V4L2Stream {
|
||||||
@@ -513,90 +558,86 @@ impl Drop for V4L2Stream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamInnerTrait for V4L2Stream {
|
|
||||||
fn configuration(&self) -> &Option<StreamConfiguration> {
|
|
||||||
&None
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn receiver(&self) -> Arc<Receiver<NokhwaResult<FrameBuffer>>> {
|
|
||||||
self.receiver.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop(&mut self) -> NokhwaResult<()> {
|
|
||||||
self.control.send(()).map_err(|why| NokhwaError::StreamShutdownError(why.to_string()))?;
|
|
||||||
loop {
|
|
||||||
if self.thread.is_finished() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sleep(Duration::from_millis(1))
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Capture for V4L2Camera {
|
impl Capture for V4L2Camera {
|
||||||
fn open_stream(&mut self) -> Result<Arc<StreamHandle>, NokhwaError> {
|
fn open_stream(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<StreamHandle, NokhwaError> {
|
||||||
if self.stream.is_some() {
|
if let Some(_) = self.stream {
|
||||||
return Err(NokhwaError::OpenStreamError("Stream Already Open".to_string()))
|
return Err(NokhwaError::OpenStreamError("StreamAlreadyOpen".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let stream_config = stream_configuration.unwrap_or_default();
|
||||||
|
|
||||||
let format = match self.camera_format {
|
let format = match self.camera_format {
|
||||||
Some(fmt) => fmt,
|
Some(fmt) => fmt,
|
||||||
None => return Err(NokhwaError::OpenStreamError("No Format".to_string()))
|
None => return Err(NokhwaError::OpenStreamError("No Format".to_string()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (control, ctrl_recv) = bounded::<()>(1);
|
let (control, ctrl_recv) = bounded(1);
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = match stream_config.bound {
|
||||||
let receiver = Arc::new(receiver);
|
StreamBounds::Bounded(b) => bounded(b as usize),
|
||||||
|
StreamBounds::Unbounded => unbounded(),
|
||||||
|
};
|
||||||
|
|
||||||
self.set_format(format)?;
|
let control = Arc::new(control);
|
||||||
|
|
||||||
let mut mmap_stream = MmapStream::new(&self.device, v4l::buffer::Type::VideoCapture).map_err(|why| {
|
let mut mmap_stream = MmapStream::new(&self.device, v4l::buffer::Type::VideoCapture).map_err(|why| {
|
||||||
return NokhwaError::OpenStreamError(why.to_string())
|
return NokhwaError::OpenStreamError(why.to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let stream_handle = StreamHandle::new(receiver, control.clone(), stream_config, format);
|
||||||
|
|
||||||
let thread = std::thread::spawn(move || {
|
let thread = std::thread::spawn(move || {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if ctrl_recv.is_disconnected() || sender.is_disconnected() {
|
if ctrl_recv.is_disconnected() || sender.is_disconnected() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Ok(_) = ctrl_recv.try_recv() {
|
if let Ok(_) = ctrl_recv.try_recv() {
|
||||||
|
let _ = sender.send(Event::Closed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match mmap_stream.next() {
|
match mmap_stream.next() {
|
||||||
Ok((data, _meta)) => { // TODO: Add metadata
|
Ok((data, meta)) => {
|
||||||
if let Err(_why) = sender.send(Ok(FrameBuffer::new(data))) {
|
let data = Cow::Owned(data.to_owned());
|
||||||
return ();
|
let mut metadata = Metadata::new();
|
||||||
}
|
|
||||||
|
metadata.insert(CompactString::from("flags"), ControlValue::BitMask(meta.flags.bits() as u64));
|
||||||
|
metadata.insert(CompactString::from("time_secs"), ControlValue::Integer(meta.timestamp.sec));
|
||||||
|
metadata.insert(CompactString::from("time_usecs"), ControlValue::Integer(meta.timestamp.usec));
|
||||||
|
metadata.insert(CompactString::from("size"), ControlValue::Integer(meta.bytesused as i64));
|
||||||
|
metadata.insert(CompactString::from("sequence"), ControlValue::Integer(meta.sequence as i64));
|
||||||
|
metadata.insert(CompactString::from("field"), ControlValue::Integer(meta.field as i64));
|
||||||
|
|
||||||
|
let _ = sender.send(Event::NewFrame(FrameBuffer::new(data, Some(metadata))));
|
||||||
}
|
}
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
if let Err(_why) = sender.send(Err(NokhwaError::ReadFrameError(why.to_string()))) {
|
let _ = sender.send(Event::Error(Box::new(why)));
|
||||||
return ();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let stream = Arc::new(StreamHandle::new(Box::new(V4L2Stream {
|
self.stream = Some(
|
||||||
|
V4L2Stream {
|
||||||
thread,
|
thread,
|
||||||
control,
|
control,
|
||||||
receiver,
|
}
|
||||||
})));
|
);
|
||||||
|
|
||||||
|
Ok(stream_handle)
|
||||||
|
|
||||||
self.stream = Some(stream.clone());
|
|
||||||
Ok(stream)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_stream(&mut self) -> Result<(), NokhwaError> {
|
fn close_stream(&mut self) -> Result<(), NokhwaError> {
|
||||||
if let Some(stream) = self.stream.clone() {
|
let mut stream = match std::mem::take(&mut self.stream) {
|
||||||
stream.stop_stream()?;
|
Some(s) => s,
|
||||||
|
None => return Err(NokhwaError::StreamShutdownError("No stream to shutdown".to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = stream.control.send(());
|
||||||
|
|
||||||
|
stream.thread.join().map_err(|why| NokhwaError::StreamShutdownError(format!("{why:?}")))?;
|
||||||
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ thiserror = "2.0"
|
|||||||
flume = "0.11"
|
flume = "0.11"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
ordered-float = "5"
|
ordered-float = "5"
|
||||||
typed-builder = "0.20"
|
typed-builder = "0.21"
|
||||||
|
compact_str = "0.9"
|
||||||
|
|
||||||
[dependencies.num-rational]
|
[dependencies.num-rational]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
@@ -47,7 +48,7 @@ features = ["derive"]
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.wgpu]
|
[dependencies.wgpu]
|
||||||
version = "24"
|
version = "25"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.opencv]
|
[dependencies.opencv]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::control::{ControlDescription, ControlId, ControlValue, Controls};
|
use crate::control::{ControlDescription, ControlId, ControlValue, Controls};
|
||||||
use crate::error::NokhwaError;
|
use crate::error::NokhwaError;
|
||||||
use crate::frame_format::FrameFormat;
|
use crate::frame_format::FrameFormat;
|
||||||
use crate::stream::StreamHandle;
|
use crate::stream::{StreamConfiguration, StreamHandle};
|
||||||
use crate::types::{CameraFormat, FrameRate, Resolution};
|
use crate::types::{CameraFormat, FrameRate, Resolution};
|
||||||
use std::collections::hash_map::{Keys, Values};
|
use std::collections::hash_map::{Keys, Values};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -15,7 +15,7 @@ pub trait Setting {
|
|||||||
frame_format: FrameFormat,
|
frame_format: FrameFormat,
|
||||||
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
||||||
|
|
||||||
fn set_format(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||||
|
|
||||||
fn control_ids(&self) -> Keys<ControlId, ControlDescription>;
|
fn control_ids(&self) -> Keys<ControlId, ControlDescription>;
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ pub trait AsyncSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Capture {
|
pub trait Capture {
|
||||||
// Implementations MUST guarantee that there can only ever be one stream open at once.
|
/// Implementations MUST guarantee that there can only ever be one stream open at once.
|
||||||
fn open_stream(&mut self) -> Result<Arc<StreamHandle>, NokhwaError>;
|
fn open_stream(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<Arc<StreamHandle>, NokhwaError>;
|
||||||
|
|
||||||
// Implementations MUST be multi-close tolerant.
|
// Implementations MUST be multi-close tolerant.
|
||||||
fn close_stream(&mut self) -> Result<(), NokhwaError>;
|
fn close_stream(&mut self) -> Result<(), NokhwaError>;
|
||||||
@@ -63,7 +63,7 @@ pub trait Capture {
|
|||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub trait AsyncStream {
|
pub trait AsyncStream {
|
||||||
async fn open_stream_async(&mut self) -> Result<StreamHandle, NokhwaError>;
|
async fn open_stream_async(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<StreamHandle, NokhwaError>;
|
||||||
|
|
||||||
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
|
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-12
@@ -112,20 +112,30 @@ impl Controls {
|
|||||||
self.descriptions.keys()
|
self.descriptions.keys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validate(&self, control_id: &ControlId, value: &ControlValue) -> Result<bool, NokhwaError> {
|
||||||
|
let description = match self.descriptions.get(control_id) {
|
||||||
|
Some(desc) => desc,
|
||||||
|
None => return Err(NokhwaError::GetPropertyError {
|
||||||
|
property: control_id.to_string(),
|
||||||
|
error: "ID Not Found".to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let None = self.values.get(control_id) {
|
||||||
|
return Err(NokhwaError::GetPropertyError {
|
||||||
|
property: control_id.to_string(),
|
||||||
|
error: "ID Not Found".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(description.validate(value))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_control_value(
|
pub fn set_control_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
control_id: &ControlId,
|
control_id: &ControlId,
|
||||||
value: ControlValue,
|
value: ControlValue,
|
||||||
) -> NokhwaResult<()> {
|
) -> NokhwaResult<()> {
|
||||||
// see if it exists
|
|
||||||
if let None = self.descriptions.get(control_id) {
|
|
||||||
return Err(NokhwaError::SetPropertyError {
|
|
||||||
property: control_id.to_string(),
|
|
||||||
value: value.to_string(),
|
|
||||||
error: "ID Not Found".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.values.get_mut(control_id) {
|
match self.values.get_mut(control_id) {
|
||||||
Some(old) => {
|
Some(old) => {
|
||||||
*old = value;
|
*old = value;
|
||||||
@@ -135,8 +145,7 @@ impl Controls {
|
|||||||
None => Err(NokhwaError::SetPropertyError {
|
None => Err(NokhwaError::SetPropertyError {
|
||||||
property: control_id.to_string(),
|
property: control_id.to_string(),
|
||||||
value: value.to_string(),
|
value: value.to_string(),
|
||||||
error: "If you got this, its probably a bug or your camera is _horribly_ bugged :>"
|
error: "ID Not Found".to_string(),
|
||||||
.to_string(),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,7 +326,7 @@ impl ControlValueDescriptor {
|
|||||||
pub enum ControlValue {
|
pub enum ControlValue {
|
||||||
Null,
|
Null,
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
BitMask(i64),
|
BitMask(u64),
|
||||||
Float(OrderedFloat<f64>),
|
Float(OrderedFloat<f64>),
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
|||||||
@@ -63,3 +63,23 @@ pub enum NokhwaError {
|
|||||||
#[error("Permission denied by user.")]
|
#[error("Permission denied by user.")]
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// 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 {}
|
||||||
|
|||||||
@@ -13,16 +13,19 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use crate::frame_format::FrameFormat;
|
use crate::frame_format::FrameFormat;
|
||||||
use small_map::{FxSmallMap, Iter};
|
use small_map::{FxSmallMap, Iter};
|
||||||
use crate::control::ControlValue;
|
use crate::control::ControlValue;
|
||||||
|
|
||||||
|
pub use compact_str::CompactString;
|
||||||
|
|
||||||
pub type PlatformSpecificFlag = u32;
|
pub type PlatformSpecificFlag = u32;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
flags: FxSmallMap<8, u32, ControlValue>,
|
flags: FxSmallMap<8, CompactString, ControlValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
impl Metadata {
|
||||||
@@ -32,15 +35,15 @@ impl Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: u32) -> Option<&ControlValue> {
|
pub fn get(&self, key: CompactString) -> Option<&ControlValue> {
|
||||||
self.flags.get(&key)
|
self.flags.get(&key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, key: u32, value: ControlValue) {
|
pub fn insert(&mut self, key: CompactString, value: ControlValue) {
|
||||||
self.flags.insert(key, value);
|
self.flags.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> Iter<'_, 8, u32, ControlValue> {
|
pub fn iter(&self) -> Iter<'_, 8, CompactString, ControlValue> {
|
||||||
self.flags.iter()
|
self.flags.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +51,7 @@ impl Metadata {
|
|||||||
impl Hash for Metadata {
|
impl Hash for Metadata {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
for (key, value) in self.flags {
|
for (key, value) in self.flags {
|
||||||
state.write_u32(key);
|
state.write(key.as_bytes());
|
||||||
value.hash(state);
|
value.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +78,7 @@ impl PartialEq for Metadata {
|
|||||||
/// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames.
|
/// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames.
|
||||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||||
pub struct FrameBuffer {
|
pub struct FrameBuffer {
|
||||||
buffer: Vec<u8>,
|
buffer: Cow<'static, [u8]>,
|
||||||
metadata: Option<Metadata>,
|
metadata: Option<Metadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +86,7 @@ impl FrameBuffer {
|
|||||||
/// Creates a new buffer with a [`&[u8]`].
|
/// Creates a new buffer with a [`&[u8]`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(buffer: Vec<u8>, metadata: Option<Metadata>) -> Self {
|
pub fn new(buffer: Cow<'static, [u8]>, metadata: Option<Metadata>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
metadata,
|
metadata,
|
||||||
@@ -96,9 +99,8 @@ impl FrameBuffer {
|
|||||||
&self.buffer
|
&self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn consume(self) -> (Cow<'static, [u8]>, Option<Metadata>) {
|
||||||
pub fn consume(self) -> Vec<u8> {
|
return (self.buffer, self.metadata)
|
||||||
self.buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use flume::{Receiver, Sender, TryRecvError};
|
use flume::{Receiver, Sender, TryRecvError};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
@@ -77,6 +78,8 @@ pub enum Event {
|
|||||||
Terminating,
|
Terminating,
|
||||||
/// The stream is closed.
|
/// The stream is closed.
|
||||||
Closed,
|
Closed,
|
||||||
|
/// An error from the driver
|
||||||
|
Error(Box<dyn std::error::Error>),
|
||||||
/// Some other message sent by the driver. This can be ignored, although logging this is preferable.
|
/// Some other message sent by the driver. This can be ignored, although logging this is preferable.
|
||||||
Other(String)
|
Other(String)
|
||||||
}
|
}
|
||||||
@@ -96,14 +99,14 @@ pub enum Event {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StreamHandle {
|
pub struct StreamHandle {
|
||||||
frame: Receiver<Event>,
|
frame: Receiver<Event>,
|
||||||
control: Sender<()>,
|
control: Arc<Sender<()>>,
|
||||||
configuration: StreamConfiguration,
|
configuration: StreamConfiguration,
|
||||||
format: Cell<CameraFormat>,
|
format: Cell<CameraFormat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandle {
|
impl StreamHandle {
|
||||||
/// You shouldn't be here.
|
/// You shouldn't be here.
|
||||||
pub fn new(recv: Receiver<Event>, control: Sender<()>, configuration: StreamConfiguration, format: CameraFormat) -> Self {
|
pub fn new(recv: Receiver<Event>, control: Arc<Sender<()>>, configuration: StreamConfiguration, format: CameraFormat) -> Self {
|
||||||
Self {
|
Self {
|
||||||
frame: recv,
|
frame: recv,
|
||||||
control,
|
control,
|
||||||
|
|||||||
Reference in New Issue
Block a user