mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
intermidiate
This commit is contained in:
Generated
+22
@@ -1307,6 +1307,7 @@ version = "0.2.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"flume",
|
"flume",
|
||||||
|
"futures-core",
|
||||||
"image",
|
"image",
|
||||||
"num-rational",
|
"num-rational",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1315,6 +1316,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"small-map",
|
"small-map",
|
||||||
"thiserror 2.0.0",
|
"thiserror 2.0.0",
|
||||||
|
"typed-builder",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1949,6 +1951,26 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-builder"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75"
|
||||||
|
dependencies = [
|
||||||
|
"typed-builder-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-builder-macro"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "udev"
|
name = "udev"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ 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::Stream;
|
use nokhwa_core::stream::{StreamHandle, StreamConfiguration, StreamInnerTrait};
|
||||||
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::JoinHandle;
|
use std::thread::{sleep, JoinHandle};
|
||||||
|
use std::time::Duration;
|
||||||
use flume::{Sender, Receiver, 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};
|
||||||
@@ -348,7 +349,7 @@ pub struct V4L2Camera {
|
|||||||
camera_format: Option<CameraFormat>,
|
camera_format: Option<CameraFormat>,
|
||||||
camera_index: CameraIndex,
|
camera_index: CameraIndex,
|
||||||
controls: Controls,
|
controls: Controls,
|
||||||
stream: Option<Stream>,
|
stream: Option<Arc<StreamHandle>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Setting for V4L2Camera {
|
impl Setting for V4L2Camera {
|
||||||
@@ -503,7 +504,7 @@ impl Setting for V4L2Camera {
|
|||||||
struct V4L2Stream {
|
struct V4L2Stream {
|
||||||
thread: JoinHandle<()>,
|
thread: JoinHandle<()>,
|
||||||
control: Sender<()>,
|
control: Sender<()>,
|
||||||
receiver: Arc<Receiver<FrameBuffer>>,
|
receiver: Arc<Receiver<NokhwaResult<FrameBuffer>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for V4L2Stream {
|
impl Drop for V4L2Stream {
|
||||||
@@ -512,14 +513,41 @@ 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<Stream, NokhwaError> {
|
fn open_stream(&mut self) -> Result<Arc<StreamHandle>, NokhwaError> {
|
||||||
|
if self.stream.is_some() {
|
||||||
|
return Err(NokhwaError::OpenStreamError("Stream Already Open".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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) = unbounded();
|
||||||
let receiver = Arc::new(receiver);
|
let receiver = Arc::new(receiver);
|
||||||
|
|
||||||
@@ -530,7 +558,6 @@ impl Capture for V4L2Camera {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
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;
|
||||||
@@ -540,18 +567,37 @@ impl Capture for V4L2Camera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match mmap_stream.next() {
|
match mmap_stream.next() {
|
||||||
Ok((data, meta)) => {
|
Ok((data, _meta)) => { // TODO: Add metadata
|
||||||
FrameBuffer::new()
|
if let Err(_why) = sender.send(Ok(FrameBuffer::new(data))) {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(why) => {
|
||||||
|
if let Err(_why) = sender.send(Err(NokhwaError::ReadFrameError(why.to_string()))) {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
()
|
return ();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
let stream = Arc::new(StreamHandle::new(Box::new(V4L2Stream {
|
||||||
|
thread,
|
||||||
|
control,
|
||||||
|
receiver,
|
||||||
|
})));
|
||||||
|
|
||||||
|
self.stream = Some(stream.clone());
|
||||||
|
Ok(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_stream(&mut self) -> Result<(), NokhwaError> {
|
fn close_stream(&mut self) -> Result<(), NokhwaError> {
|
||||||
todo!()
|
if let Some(stream) = self.stream.clone() {
|
||||||
|
stream.stop_stream()?;
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ serialize = ["serde"]
|
|||||||
wgpu-types = ["wgpu"]
|
wgpu-types = ["wgpu"]
|
||||||
opencv-mat = ["opencv", "opencv/clang-runtime"]
|
opencv-mat = ["opencv", "opencv/clang-runtime"]
|
||||||
docs-features = ["serialize", "wgpu-types"]
|
docs-features = ["serialize", "wgpu-types"]
|
||||||
async = ["async-trait", "flume/async"]
|
async = ["async-trait", "flume/async", "futures-core"]
|
||||||
test-fail-warnings = []
|
test-fail-warnings = []
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ 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"
|
||||||
|
|
||||||
[dependencies.num-rational]
|
[dependencies.num-rational]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
@@ -58,7 +59,9 @@ optional = true
|
|||||||
version = "0.1"
|
version = "0.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.futures-core]
|
||||||
|
version = "0.3"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["docs-features"]
|
features = ["docs-features"]
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
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::Stream;
|
use crate::stream::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;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait Setting {
|
pub trait Setting {
|
||||||
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
||||||
@@ -33,7 +34,6 @@ pub trait Setting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
pub trait AsyncSetting {
|
pub trait AsyncSetting {
|
||||||
async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
||||||
|
|
||||||
@@ -55,16 +55,15 @@ 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<Stream, NokhwaError>;
|
fn open_stream(&mut self) -> 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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
pub trait AsyncStream {
|
pub trait AsyncStream {
|
||||||
async fn open_stream_async(&mut self) -> Result<Stream, NokhwaError>;
|
async fn open_stream_async(&mut self) -> Result<StreamHandle, NokhwaError>;
|
||||||
|
|
||||||
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
|
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
|
||||||
}
|
}
|
||||||
@@ -72,5 +71,4 @@ pub trait AsyncStream {
|
|||||||
pub trait Camera: Setting + Capture {}
|
pub trait Camera: Setting + Capture {}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {}
|
pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {}
|
||||||
|
|||||||
+54
-94
@@ -1,99 +1,59 @@
|
|||||||
use crate::{error::NokhwaError, frame_buffer::FrameBuffer, frame_format::FrameFormat};
|
use std::borrow::Cow;
|
||||||
use image::{ImageBuffer, Pixel};
|
use std::fmt::Debug;
|
||||||
use std::ops::{ControlFlow, Deref};
|
use crate::error::NokhwaError;
|
||||||
|
use crate::frame_buffer::FrameBuffer;
|
||||||
|
use crate::frame_format::FrameFormat;
|
||||||
|
use crate::stream::{StreamHandle};
|
||||||
|
use crate::types::{CameraFormat, FrameRate, Resolution};
|
||||||
|
|
||||||
/// Trait to define a struct that can decode a [`FrameBuffer`]
|
#[derive(Debug)]
|
||||||
pub trait Decoder<OutputPixel: Pixel> {
|
pub struct Decoder<'stream, Video> where
|
||||||
/// Formats that the decoder can decode.
|
Video: Codec {
|
||||||
|
video: Video,
|
||||||
|
stream: &'stream mut StreamHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'stream, Video> Decoder<'stream, Video> where Video: Codec {
|
||||||
|
pub fn new(stream: &'stream mut StreamHandle, decoder: Video) -> Result<Self, NokhwaError> {
|
||||||
|
let format = stream.format();
|
||||||
|
|
||||||
|
let mut decoder = decoder;
|
||||||
|
decoder.initialize(format)?;
|
||||||
|
Ok(Self { video: decoder, stream })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DecoderAsync<'stream, Video> where
|
||||||
|
Video: CodecAsync {
|
||||||
|
video: Video,
|
||||||
|
stream_handle: &'stream mut StreamHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Codec: Debug {
|
||||||
const ALLOWED_FORMATS: &'static [FrameFormat];
|
const ALLOWED_FORMATS: &'static [FrameFormat];
|
||||||
|
|
||||||
/// Container type for the decoder. Will be used for ImageBuffer
|
fn initialize(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||||
type PixelContainer: Deref<Target = [OutputPixel::Subpixel]>;
|
|
||||||
|
fn stop(&mut self) -> Result<(), NokhwaError>;
|
||||||
fn check_format(buffer: &FrameBuffer) -> ControlFlow<NokhwaError> {
|
|
||||||
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
|
fn frame_format(&self) -> Result<FrameFormat, NokhwaError>;
|
||||||
return ControlFlow::Break(NokhwaError::ConversionError("unsupported".to_string()));
|
|
||||||
}
|
fn resolution(&self) -> Result<Resolution, NokhwaError>;
|
||||||
|
|
||||||
ControlFlow::Continue(())
|
fn frame_rate(&self) -> Result<FrameRate, NokhwaError>;
|
||||||
}
|
|
||||||
|
fn set_frame_format(&mut self, frame_format: FrameFormat) -> Result<(), NokhwaError>;
|
||||||
/// Decode function.
|
|
||||||
fn decode(
|
fn set_resolution(&mut self, resolution: Resolution) -> Result<(), NokhwaError>;
|
||||||
&mut self,
|
|
||||||
buffer: &FrameBuffer,
|
fn set_frame_rate(&mut self, frame_rate: FrameRate) -> Result<(), NokhwaError>;
|
||||||
) -> Result<ImageBuffer<OutputPixel, Self::PixelContainer>, NokhwaError>;
|
|
||||||
|
fn decode_frame(&mut self, buffer: &FrameBuffer) -> Result<Cow<'_, [u8]>, NokhwaError>;
|
||||||
/// Decode to user-provided Buffer
|
|
||||||
///
|
|
||||||
/// Incase that the buffer is not large enough this should error.
|
|
||||||
fn decode_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
output: &mut [OutputPixel::Subpixel],
|
|
||||||
) -> Result<(), NokhwaError>;
|
|
||||||
|
|
||||||
/// Decoder Predicted Size
|
|
||||||
fn predicted_size_of_frame(buffer: &FrameBuffer) -> Option<usize> {
|
|
||||||
if !Self::ALLOWED_FORMATS.contains(&buffer.source_frame_format()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let res = buffer.resolution();
|
|
||||||
Some(
|
|
||||||
res.x() as usize
|
|
||||||
* res.y() as usize
|
|
||||||
* size_of::<OutputPixel::Subpixel>()
|
|
||||||
* OutputPixel::CHANNEL_COUNT as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decoder that can be used statically (struct contains no state)
|
|
||||||
///
|
|
||||||
/// This is useful for times that a simple function is all that is required.
|
|
||||||
pub trait StaticDecoder<OutputPixel: Pixel>: Decoder<OutputPixel> {
|
|
||||||
fn decode_static(
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
) -> Result<ImageBuffer<OutputPixel, Self::PixelContainer>, NokhwaError>;
|
|
||||||
|
|
||||||
fn decode_static_to_buffer(
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
output: &mut [OutputPixel::Subpixel],
|
|
||||||
) -> Result<(), NokhwaError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
pub trait CodecAsync: Codec + Debug {}
|
||||||
pub trait AsyncDecoder<OutputPixel: Pixel>: Decoder<OutputPixel> {
|
|
||||||
/// Asynchronous decoder
|
|
||||||
async fn decode_async(
|
|
||||||
&mut self,
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
) -> Result<ImageBuffer<OutputPixel, Self::PixelContainer>, NokhwaError>;
|
|
||||||
|
|
||||||
/// Asynchronous decoder to user buffer.
|
|
||||||
async fn decode_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
output: &mut [OutputPixel::Subpixel],
|
|
||||||
) -> Result<(), NokhwaError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
pub trait AsyncStaticDecoder<OutputPixel: Pixel>:
|
|
||||||
Decoder<OutputPixel> + AsyncDecoder<OutputPixel>
|
|
||||||
{
|
|
||||||
/// Asynchronous decoder
|
|
||||||
async fn decode_static_async(
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
) -> Result<ImageBuffer<OutputPixel, Self::PixelContainer>, NokhwaError>;
|
|
||||||
|
|
||||||
/// Asynchronous decoder to user buffer.
|
|
||||||
async fn decode_static_buffer_async(
|
|
||||||
buffer: &FrameBuffer,
|
|
||||||
output: &mut [OutputPixel::Subpixel],
|
|
||||||
) -> Result<(), NokhwaError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(feature = "decoders")]
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use crate::frame_format::FrameFormat;
|
use crate::frame_format::FrameFormat;
|
||||||
use crate::types::Resolution;
|
|
||||||
use small_map::{FxSmallMap, Iter};
|
use small_map::{FxSmallMap, Iter};
|
||||||
use crate::control::ControlValue;
|
use crate::control::ControlValue;
|
||||||
|
|
||||||
@@ -76,9 +75,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 {
|
||||||
resolution: Resolution,
|
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
source_frame_format: FrameFormat,
|
|
||||||
metadata: Option<Metadata>,
|
metadata: Option<Metadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,21 +83,13 @@ 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(resolution: Resolution, buffer: Vec<u8>, source_frame_format: FrameFormat, metadata: Option<Metadata>) -> Self {
|
pub fn new(buffer: Vec<u8>, metadata: Option<Metadata>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
resolution,
|
|
||||||
buffer,
|
buffer,
|
||||||
source_frame_format,
|
|
||||||
metadata,
|
metadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`Resolution`] of this buffer.
|
|
||||||
#[must_use]
|
|
||||||
pub fn resolution(&self) -> Resolution {
|
|
||||||
self.resolution
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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] {
|
||||||
@@ -117,9 +106,4 @@ impl FrameBuffer {
|
|||||||
self.metadata.as_ref()
|
self.metadata.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`SourceFrameFormat`] of this buffer.
|
|
||||||
#[must_use]
|
|
||||||
pub fn source_frame_format(&self) -> FrameFormat {
|
|
||||||
self.source_frame_format
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![allow(clippy::cast_precision_loss)]
|
|
||||||
#![allow(clippy::cast_sign_loss)]
|
|
||||||
#![allow(clippy::cast_possible_truncation)]
|
|
||||||
#![cfg_attr(feature = "test-fail-warning", deny(warnings))]
|
#![cfg_attr(feature = "test-fail-warning", deny(warnings))]
|
||||||
// #![cfg_attr(feature = "docs-features", feature(doc_cfg))]
|
#![cfg_attr(feature = "docs-features", feature(doc_cfg))]
|
||||||
/*
|
/*
|
||||||
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
|
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ pub trait PlatformTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
|
||||||
pub trait AsyncPlatformTrait {
|
pub trait AsyncPlatformTrait {
|
||||||
const PLATFORM: Backends;
|
const PLATFORM: Backends;
|
||||||
type AsyncCamera: AsyncCamera;
|
type AsyncCamera: AsyncCamera;
|
||||||
|
|||||||
+177
-67
@@ -1,95 +1,205 @@
|
|||||||
use crate::error::{NokhwaError, NokhwaResult};
|
use std::cell::Cell;
|
||||||
|
use std::time::Duration;
|
||||||
|
use flume::{Receiver, Sender, TryRecvError};
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
use crate::error::NokhwaError;
|
||||||
use crate::frame_buffer::FrameBuffer;
|
use crate::frame_buffer::FrameBuffer;
|
||||||
use std::sync::Arc;
|
use crate::types::CameraFormat;
|
||||||
use derive_builder::Builder;
|
|
||||||
use flume::{Receiver, TryRecvError};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Builder)]
|
/// 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 {
|
pub struct StreamConfiguration {
|
||||||
buffer_size: Option<u32>,
|
#[builder(default)]
|
||||||
|
pub receiver: StreamReceiverBehaviour,
|
||||||
|
#[builder(default)]
|
||||||
|
pub bound: StreamBounds,
|
||||||
|
#[builder(default)]
|
||||||
|
pub on_other: ControlFlowOnOther,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StreamInnerTrait {
|
/// Possible events to receive from an active stream.
|
||||||
fn configuration(&self) -> &StreamConfiguration;
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
fn receiver(&self) -> Arc<Receiver<FrameBuffer>>;
|
pub enum Event {
|
||||||
fn stop(&mut self) -> NokhwaResult<()>;
|
/// A new frame.
|
||||||
|
NewFrame(FrameBuffer),
|
||||||
|
/// 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,
|
||||||
|
/// Some other message sent by the driver. This can be ignored, although logging this is preferable.
|
||||||
|
Other(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Stream {
|
/// Represents a handle to a currently open stream.
|
||||||
inner: Box<dyn StreamInnerTrait>,
|
///
|
||||||
|
/// 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 {
|
||||||
|
frame: Receiver<Event>,
|
||||||
|
control: Sender<()>,
|
||||||
|
configuration: StreamConfiguration,
|
||||||
|
format: Cell<CameraFormat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl StreamHandle {
|
||||||
pub fn new(inner: Box<dyn StreamInnerTrait>) -> Self {
|
/// You shouldn't be here.
|
||||||
|
pub fn new(recv: Receiver<Event>, control: Sender<()>, configuration: StreamConfiguration, format: CameraFormat) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
frame: recv,
|
||||||
|
control,
|
||||||
|
configuration,
|
||||||
|
format: Cell::new(format),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_disconnected(&self) -> NokhwaResult<()> {
|
pub fn configuration(&self) -> &StreamConfiguration {
|
||||||
if self.inner.receiver().is_disconnected() {
|
&self.configuration
|
||||||
return Err(NokhwaError::ReadFrameError(
|
}
|
||||||
"stream is disconnected!".to_string(),
|
|
||||||
))
|
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().map_or_else(|_| { Event::Closed }, |e| { e })
|
||||||
|
}
|
||||||
|
StreamReceiverBehaviour::Timeout(time) => {
|
||||||
|
self.frame.recv_timeout(time).map_or_else(|_| { Event::NotReady }, |e| { e })
|
||||||
|
}
|
||||||
|
StreamReceiverBehaviour::Try => {
|
||||||
|
self.frame.try_recv().map_or_else(|why| {
|
||||||
|
match why {
|
||||||
|
TryRecvError::Empty => Event::NotReady,
|
||||||
|
TryRecvError::Disconnected => Event::Closed,
|
||||||
|
}
|
||||||
|
}, |e| { e })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Event::FormatChange(fmt) = event {
|
||||||
|
self.format.set(fmt);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
return Ok(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_frame(&self) -> NokhwaResult<FrameBuffer> {
|
pub fn next_frame(&self) -> Result<FrameBuffer, NokhwaError> {
|
||||||
self.check_disconnected()?;
|
loop {
|
||||||
|
let event = self.next_event()?;
|
||||||
self.inner
|
match event {
|
||||||
.receiver()
|
Event::NewFrame(f) => return Ok(f),
|
||||||
.recv()
|
Event::FormatChange(_) | Event::NotReady => continue,
|
||||||
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))
|
Event::Terminating | Event::Closed => {
|
||||||
}
|
let _ = self.control.try_send(());
|
||||||
|
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()))
|
||||||
pub fn try_poll_frame(&self) -> NokhwaResult<Option<FrameBuffer>> {
|
}
|
||||||
self.check_disconnected()?;
|
Event::Other(why) => {
|
||||||
|
match self.configuration.on_other {
|
||||||
if self.inner.receiver().is_empty() {
|
ControlFlowOnOther::Continue => continue,
|
||||||
return Ok(None);
|
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why))
|
||||||
}
|
}
|
||||||
|
|
||||||
let possible_frame = self.inner
|
|
||||||
.receiver()
|
|
||||||
.try_recv();
|
|
||||||
|
|
||||||
match possible_frame {
|
|
||||||
Ok(f) => Ok(Some(f)),
|
|
||||||
Err(why) => {
|
|
||||||
match why {
|
|
||||||
TryRecvError::Empty => Ok(None),
|
|
||||||
TryRecvError::Disconnected => Err(NokhwaError::ReadFrameError(
|
|
||||||
"stream is disconnected!".to_string(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub async fn await_frame(&self) -> NokhwaResult<FrameBuffer> {
|
pub async fn poll_event(&self) -> Result<Event, NokhwaError> {
|
||||||
use futures::TryFutureExt;
|
Ok(self.frame.recv_async().await.map_or_else(|_| { Event::Closed }, |e| { if let Event::FormatChange(fmt) = e {
|
||||||
|
self.format.set(fmt);
|
||||||
self.check_disconnected()?;
|
}
|
||||||
|
e
|
||||||
self.inner
|
}))
|
||||||
.receiver()
|
|
||||||
.recv_async()
|
|
||||||
.map_err(|why| NokhwaError::ReadFrameError(why.to_string())).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_stream(mut self) -> NokhwaResult<()> {
|
// TODO: a smarter implementation? maybe?
|
||||||
self.inner.stop()?;
|
#[cfg(feature = "async")]
|
||||||
Ok(())
|
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::FormatChange(_) | Event::NotReady => continue,
|
||||||
|
Event::Terminating | Event::Closed => {
|
||||||
|
let _ = self.control.try_send(());
|
||||||
|
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()))
|
||||||
|
}
|
||||||
|
Event::Other(why) => {
|
||||||
|
match self.configuration.on_other {
|
||||||
|
ControlFlowOnOther::Continue => continue,
|
||||||
|
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Stream {
|
impl Drop for StreamHandle {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _ = self.inner.stop();
|
let _ = self.control.try_send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user