mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27: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-config.yaml
|
||||
|
||||
# this is a library so its okgit
|
||||
Cargo.lock
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu]
|
||||
version = "24"
|
||||
version = "25"
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
use std::borrow::Cow;
|
||||
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::frame_format::FrameFormat;
|
||||
use nokhwa_core::platform::{Backends, PlatformTrait};
|
||||
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 std::collections::hash_map::{Keys, Values};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::num::NonZeroI32;
|
||||
use std::sync::Arc;
|
||||
use std::thread::{sleep, JoinHandle};
|
||||
use std::time::Duration;
|
||||
use flume::{Sender, Receiver, unbounded, bounded};
|
||||
use std::thread::JoinHandle;
|
||||
use flume::{Sender, unbounded, bounded};
|
||||
use v4l::context::enum_devices;
|
||||
use v4l::control::{Description, Flags, MenuItem, Type, Value};
|
||||
use v4l::frameinterval::FrameIntervalEnum;
|
||||
use v4l::video::output::Parameters;
|
||||
use v4l::video::Output;
|
||||
use v4l::{Capabilities, 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 v4l::{Capabilities, Control, Device, Format, FourCC, Fraction, FrameInterval};
|
||||
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::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 {
|
||||
let name = capabilities.card;
|
||||
@@ -249,7 +249,7 @@ fn convert_description_to_ctrl_body(description: Description) -> Option<ControlD
|
||||
Type::Bitmask => {
|
||||
(
|
||||
ControlValueDescriptor::BitMask,
|
||||
Some(ControlValue::BitMask(description.default))
|
||||
Some(ControlValue::BitMask(description.default as u64))
|
||||
)
|
||||
}
|
||||
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 {}
|
||||
|
||||
@@ -349,7 +377,7 @@ pub struct V4L2Camera {
|
||||
camera_format: Option<CameraFormat>,
|
||||
camera_index: CameraIndex,
|
||||
controls: Controls,
|
||||
stream: Option<Arc<StreamHandle>>,
|
||||
stream: Option<V4L2Stream>,
|
||||
}
|
||||
|
||||
impl Setting for V4L2Camera {
|
||||
@@ -419,7 +447,7 @@ impl Setting for V4L2Camera {
|
||||
}).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())?;
|
||||
self.device.set_format(
|
||||
&Format::new(camera_format.width(), camera_format.height(), fourcc)
|
||||
@@ -435,6 +463,7 @@ impl Setting for V4L2Camera {
|
||||
error: why.to_string(),
|
||||
}
|
||||
})?;
|
||||
self.camera_format = Some(camera_format);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -459,7 +488,24 @@ impl Setting for V4L2Camera {
|
||||
}
|
||||
|
||||
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> {
|
||||
@@ -503,8 +549,7 @@ impl Setting for V4L2Camera {
|
||||
|
||||
struct V4L2Stream {
|
||||
thread: JoinHandle<()>,
|
||||
control: Sender<()>,
|
||||
receiver: Arc<Receiver<NokhwaResult<FrameBuffer>>>,
|
||||
control: Arc<Sender<()>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
fn open_stream(&mut self) -> Result<Arc<StreamHandle>, NokhwaError> {
|
||||
if self.stream.is_some() {
|
||||
fn open_stream(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<StreamHandle, NokhwaError> {
|
||||
if let Some(_) = self.stream {
|
||||
return Err(NokhwaError::OpenStreamError("StreamAlreadyOpen".to_string()))
|
||||
}
|
||||
|
||||
let stream_config = stream_configuration.unwrap_or_default();
|
||||
|
||||
let format = match self.camera_format {
|
||||
Some(fmt) => fmt,
|
||||
None => return Err(NokhwaError::OpenStreamError("No Format".to_string()))
|
||||
};
|
||||
|
||||
let (control, ctrl_recv) = bounded::<()>(1);
|
||||
let (sender, receiver) = unbounded();
|
||||
let receiver = Arc::new(receiver);
|
||||
let (control, ctrl_recv) = bounded(1);
|
||||
let (sender, receiver) = match stream_config.bound {
|
||||
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| {
|
||||
return NokhwaError::OpenStreamError(why.to_string())
|
||||
})?;
|
||||
|
||||
let stream_handle = StreamHandle::new(receiver, control.clone(), stream_config, format);
|
||||
|
||||
let thread = std::thread::spawn(move || {
|
||||
|
||||
loop {
|
||||
if ctrl_recv.is_disconnected() || sender.is_disconnected() {
|
||||
return;
|
||||
}
|
||||
if let Ok(_) = ctrl_recv.try_recv() {
|
||||
let _ = sender.send(Event::Closed);
|
||||
return;
|
||||
}
|
||||
|
||||
match mmap_stream.next() {
|
||||
Ok((data, _meta)) => { // TODO: Add metadata
|
||||
if let Err(_why) = sender.send(Ok(FrameBuffer::new(data))) {
|
||||
return ();
|
||||
}
|
||||
Ok((data, meta)) => {
|
||||
let data = Cow::Owned(data.to_owned());
|
||||
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) => {
|
||||
if let Err(_why) = sender.send(Err(NokhwaError::ReadFrameError(why.to_string()))) {
|
||||
return ();
|
||||
let _ = sender.send(Event::Error(Box::new(why)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ();
|
||||
});
|
||||
|
||||
let stream = Arc::new(StreamHandle::new(Box::new(V4L2Stream {
|
||||
self.stream = Some(
|
||||
V4L2Stream {
|
||||
thread,
|
||||
control,
|
||||
receiver,
|
||||
})));
|
||||
}
|
||||
);
|
||||
|
||||
Ok(stream_handle)
|
||||
|
||||
self.stream = Some(stream.clone());
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
fn close_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
if let Some(stream) = self.stream.clone() {
|
||||
stream.stop_stream()?;
|
||||
let mut stream = match std::mem::take(&mut self.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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ thiserror = "2.0"
|
||||
flume = "0.11"
|
||||
num-traits = "0.2"
|
||||
ordered-float = "5"
|
||||
typed-builder = "0.20"
|
||||
typed-builder = "0.21"
|
||||
compact_str = "0.9"
|
||||
|
||||
[dependencies.num-rational]
|
||||
version = "0.4"
|
||||
@@ -47,7 +48,7 @@ features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu]
|
||||
version = "24"
|
||||
version = "25"
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::control::{ControlDescription, ControlId, ControlValue, Controls};
|
||||
use crate::error::NokhwaError;
|
||||
use crate::frame_format::FrameFormat;
|
||||
use crate::stream::StreamHandle;
|
||||
use crate::stream::{StreamConfiguration, StreamHandle};
|
||||
use crate::types::{CameraFormat, FrameRate, Resolution};
|
||||
use std::collections::hash_map::{Keys, Values};
|
||||
use std::collections::HashMap;
|
||||
@@ -15,7 +15,7 @@ pub trait Setting {
|
||||
frame_format: FrameFormat,
|
||||
) -> 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>;
|
||||
|
||||
@@ -54,8 +54,8 @@ pub trait AsyncSetting {
|
||||
}
|
||||
|
||||
pub trait Capture {
|
||||
// Implementations MUST guarantee that there can only ever be one stream open at once.
|
||||
fn open_stream(&mut self) -> Result<Arc<StreamHandle>, NokhwaError>;
|
||||
/// Implementations MUST guarantee that there can only ever be one stream open at once.
|
||||
fn open_stream(&mut self, stream_configuration: Option<StreamConfiguration>) -> Result<Arc<StreamHandle>, NokhwaError>;
|
||||
|
||||
// Implementations MUST be multi-close tolerant.
|
||||
fn close_stream(&mut self) -> Result<(), NokhwaError>;
|
||||
@@ -63,7 +63,7 @@ pub trait Capture {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
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>;
|
||||
}
|
||||
|
||||
+21
-12
@@ -112,20 +112,30 @@ impl Controls {
|
||||
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(
|
||||
&mut self,
|
||||
control_id: &ControlId,
|
||||
value: ControlValue,
|
||||
) -> 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) {
|
||||
Some(old) => {
|
||||
*old = value;
|
||||
@@ -135,8 +145,7 @@ impl Controls {
|
||||
None => Err(NokhwaError::SetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "If you got this, its probably a bug or your camera is _horribly_ bugged :>"
|
||||
.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -317,7 +326,7 @@ impl ControlValueDescriptor {
|
||||
pub enum ControlValue {
|
||||
Null,
|
||||
Integer(i64),
|
||||
BitMask(i64),
|
||||
BitMask(u64),
|
||||
Float(OrderedFloat<f64>),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
|
||||
@@ -63,3 +63,23 @@ pub enum NokhwaError {
|
||||
#[error("Permission denied by user.")]
|
||||
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
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use crate::frame_format::FrameFormat;
|
||||
use small_map::{FxSmallMap, Iter};
|
||||
use crate::control::ControlValue;
|
||||
|
||||
pub use compact_str::CompactString;
|
||||
|
||||
pub type PlatformSpecificFlag = u32;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Metadata {
|
||||
flags: FxSmallMap<8, u32, ControlValue>,
|
||||
flags: FxSmallMap<8, CompactString, ControlValue>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: u32, value: ControlValue) {
|
||||
pub fn insert(&mut self, key: CompactString, value: ControlValue) {
|
||||
self.flags.insert(key, value);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_, 8, u32, ControlValue> {
|
||||
pub fn iter(&self) -> Iter<'_, 8, CompactString, ControlValue> {
|
||||
self.flags.iter()
|
||||
}
|
||||
}
|
||||
@@ -48,7 +51,7 @@ impl Metadata {
|
||||
impl Hash for Metadata {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for (key, value) in self.flags {
|
||||
state.write_u32(key);
|
||||
state.write(key.as_bytes());
|
||||
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.
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
pub struct FrameBuffer {
|
||||
buffer: Vec<u8>,
|
||||
buffer: Cow<'static, [u8]>,
|
||||
metadata: Option<Metadata>,
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@ impl FrameBuffer {
|
||||
/// Creates a new buffer with a [`&[u8]`].
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn new(buffer: Vec<u8>, metadata: Option<Metadata>) -> Self {
|
||||
pub fn new(buffer: Cow<'static, [u8]>, metadata: Option<Metadata>) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
metadata,
|
||||
@@ -96,9 +99,8 @@ impl FrameBuffer {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn consume(self) -> Vec<u8> {
|
||||
self.buffer
|
||||
pub fn consume(self) -> (Cow<'static, [u8]>, Option<Metadata>) {
|
||||
return (self.buffer, self.metadata)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::cell::Cell;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use flume::{Receiver, Sender, TryRecvError};
|
||||
use typed_builder::TypedBuilder;
|
||||
@@ -77,6 +78,8 @@ pub enum Event {
|
||||
Terminating,
|
||||
/// The stream is 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.
|
||||
Other(String)
|
||||
}
|
||||
@@ -96,14 +99,14 @@ pub enum Event {
|
||||
#[derive(Debug)]
|
||||
pub struct StreamHandle {
|
||||
frame: Receiver<Event>,
|
||||
control: Sender<()>,
|
||||
control: Arc<Sender<()>>,
|
||||
configuration: StreamConfiguration,
|
||||
format: Cell<CameraFormat>,
|
||||
}
|
||||
|
||||
impl StreamHandle {
|
||||
/// 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 {
|
||||
frame: recv,
|
||||
control,
|
||||
|
||||
Reference in New Issue
Block a user