(untested) preliminary completion of V4L2 backend. TODO: decoder API

This commit is contained in:
l1npengtul
2025-04-17 17:21:18 +09:00
parent ef219dc3bd
commit 361e26e453
9 changed files with 181 additions and 102 deletions
+3
View File
@@ -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
View File
@@ -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]
+105 -64
View File
@@ -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(
thread, V4L2Stream {
control, thread,
receiver, control,
}))); }
);
self.stream = Some(stream.clone());
Ok(stream) Ok(stream_handle)
} }
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(())
} }
} }
+3 -2
View File
@@ -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]
+5 -5
View File
@@ -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
View File
@@ -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),
+20
View File
@@ -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 {}
+18 -16
View File
@@ -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 {
@@ -30,17 +33,17 @@ impl Metadata {
Self { Self {
flags: Default::default(), flags: Default::default(),
} }
} }
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,24 +86,23 @@ 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,
} }
} }
/// Get the data of this buffer. /// Get the data of this buffer.
#[must_use] #[must_use]
pub fn buffer(&self) -> &[u8] { pub fn buffer(&self) -> &[u8] {
&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]
pub fn metadata(&self) -> Option<&Metadata> { pub fn metadata(&self) -> Option<&Metadata> {
self.metadata.as_ref() self.metadata.as_ref()
+5 -2
View File
@@ -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,