mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
add uvc, adjust traits
This commit is contained in:
Generated
+20
@@ -26,6 +26,26 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/v4l2-sys-mit-1401199a612bbed5/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/mozjpeg-sys-c35f54bf209e057e/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/nokhwa-fca4e2084c34cbf5/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/bitflags-88b50a288ea9b589/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/crc32fast-f112d4af2349618a/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/crossbeam-utils-f18a9a09c842a116/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/libc-34b92eaa97780640/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/memoffset-7dcca5ba1dbddb41/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/miniz_oxide-24ce73e20e4a3ff2/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/mozjpeg-sys-1a963405fcd77efc/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/num-integer-add9cb380db572d4/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/num-iter-7488762592985ead/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/num-rational-cc8bf80ac17a0e16/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/num-traits-68267bf3e91cd7cc/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/proc-macro2-ce1a087ba38e801a/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/rayon-82b053c11a069b7f/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/rayon-core-14a7bb24d6f58d5f/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/syn-ce42942113a88686/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/futures-core-398c829ef0b43005/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/proc-macro-error-3767db49a8a5c3f2/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/proc-macro-error-attr-4fdf86427e6e8f90/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/syn-649155a95cf88f66/out" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/target/debug/build/syn-e4c794e1e37211a0/out" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
||||
+4
-10
@@ -11,18 +11,19 @@ repository = "https://github.com/l1npengtul/nokhwa"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
input_uvc = ["uvc"]
|
||||
input_uvc = ["uvc", "uvc/vendor"]
|
||||
input_opencv = ["opencv", "opencv/clang-runtime"]
|
||||
input_msmf = ["windows"]
|
||||
input_v4l = ["v4l"]
|
||||
input_gstreamer = ["gstreamer"]
|
||||
input_ffmpeg = []
|
||||
docs-only = ["input_uvc", "input_opencv", "input_msmf", "input_v4l", "input_gstreamer", "input_ffmpeg"]
|
||||
docs-only = ["input_uvc", "input_opencv", "input_v4l", "input_gstreamer", "input_ffmpeg"]
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.24"
|
||||
image = "0.23.14"
|
||||
mozjpeg = "0.8.24"
|
||||
ouroboros = "0.9.3"
|
||||
flume = "0.10.5"
|
||||
|
||||
[dependencies.opencv]
|
||||
version = "0.53.0"
|
||||
@@ -40,13 +41,6 @@ optional = true
|
||||
version = "0.16.7"
|
||||
optional = true
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.10.0"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.windows]
|
||||
version = "0.10.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
features = ["docs-only"]
|
||||
@@ -1,2 +1,4 @@
|
||||
#[cfg(feature = "input_v4l")]
|
||||
pub mod v4l2;
|
||||
#[cfg(feature = "input_uvc")]
|
||||
pub mod uvc;
|
||||
@@ -0,0 +1,52 @@
|
||||
use crate::{CameraFormat, CameraInfo, NokhwaError};
|
||||
use flume::{Receiver, Sender};
|
||||
use ouroboros::self_referencing;
|
||||
use std::sync::{atomic::AtomicUsize, Arc};
|
||||
use uvc::{ActiveStream, Context, Device, DeviceHandle, StreamHandle, Error, DeviceList};
|
||||
|
||||
// ignore the IDE.
|
||||
/// The backend struct that interfaces with libuvc.
|
||||
/// To see what this does, please see [`CaptureBackendTrait`]
|
||||
/// # Quirks
|
||||
/// The indexing for this backend is based off of `libuvc`'s device ordering, not the OS.
|
||||
/// # Safety
|
||||
/// This backend requires use of `unsafe` due to the self-referencing structs involved.
|
||||
#[self_referencing(chain_hack, pub_extras)]
|
||||
pub struct UVCCaptureDevice<'a> {
|
||||
camera_format: Option<CameraFormat>,
|
||||
camera_info: CameraInfo,
|
||||
device_receiver: Box<Receiver<Vec<u8>>>,
|
||||
device_sender: Box<Sender<Vec<u8>>>,
|
||||
context: Box<Context<'a>>,
|
||||
#[borrows(context)]
|
||||
#[not_covariant]
|
||||
device: Box<Option<Device<'this>>>,
|
||||
#[borrows(device)]
|
||||
#[not_covariant]
|
||||
device_handle: Box<Option<DeviceHandle<'this>>>,
|
||||
#[borrows(device_handle)]
|
||||
#[not_covariant]
|
||||
stream_handle: Box<Option<StreamHandle<'this>>>,
|
||||
#[borrows(stream_handle)]
|
||||
#[not_covariant]
|
||||
active_stream: Box<Option<ActiveStream<'this, Arc<AtomicUsize>>>>,
|
||||
}
|
||||
|
||||
impl<'a> UVCCaptureDevice<'a> {
|
||||
pub fn new(index: usize) -> Result<Self, NokhwaError> {
|
||||
let context = match Context::new() {
|
||||
Ok(ctx) => Box::new(ctx),
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenDevice(why.to_string())),
|
||||
};
|
||||
|
||||
let device = match context.devices() {
|
||||
Ok(device_list) => {
|
||||
for (idx, dev) in device_list.enumerate() {
|
||||
if idx == index {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenDevice(why.to_string())),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
use crate::{CaptureBackendTrait, FrameFormat, QueryBackendTrait, Resolution, error::NokhwaError, mjpeg_to_rgb888, utils::{CameraFormat, CameraInfo}, yuyv422_to_rgb888};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
error::NokhwaError,
|
||||
mjpeg_to_rgb888,
|
||||
utils::{CameraFormat, CameraInfo},
|
||||
yuyv422_to_rgb888, CaptureBackendTrait, FrameFormat, QueryBackendTrait, Resolution,
|
||||
};
|
||||
use image::{ImageBuffer, Rgb};
|
||||
use v4l::prelude::*;
|
||||
use v4l::{
|
||||
buffer::Type,
|
||||
io::traits::CaptureStream,
|
||||
video::{capture::Parameters, Capture},
|
||||
Format, FourCC,
|
||||
};
|
||||
use v4l::{frameinterval::FrameIntervalEnum, framesize::FrameSizeEnum, prelude::*};
|
||||
|
||||
#[cfg(feature = "input_v4l")]
|
||||
impl From<CameraFormat> for Format {
|
||||
@@ -71,10 +78,6 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
self.camera_info.clone()
|
||||
}
|
||||
|
||||
fn get_camera_format(&self) -> Option<CameraFormat> {
|
||||
self.camera_format
|
||||
}
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError> {
|
||||
match self.camera_format {
|
||||
@@ -88,6 +91,10 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_camera_format(&self) -> Option<CameraFormat> {
|
||||
self.camera_format
|
||||
}
|
||||
|
||||
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
|
||||
let prev_format = match self.device.format() {
|
||||
Ok(fmt) => fmt,
|
||||
@@ -268,24 +275,75 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> QueryBackendTrait for V4LCaptureDevice<'a>{
|
||||
fn get_compatible_list_by_framerate(&self, fourcc: FrameFormat) -> std::collections::HashMap<u32, Vec<Resolution>> {
|
||||
todo!()
|
||||
impl<'a> QueryBackendTrait for V4LCaptureDevice<'a> {
|
||||
fn get_compatible_list_by_resolution(
|
||||
&self,
|
||||
fourcc: FrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
|
||||
let resolutions = self.get_resolution_list(fourcc)?;
|
||||
let format = match fourcc {
|
||||
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
|
||||
FrameFormat::YUYV => FourCC::new(b"YUYV"),
|
||||
};
|
||||
let mut resmap = HashMap::new();
|
||||
for res in resolutions {
|
||||
let mut compatible_fps = vec![];
|
||||
match self
|
||||
.device
|
||||
.enum_frameintervals(format, res.width(), res.height())
|
||||
{
|
||||
Ok(intervals) => {
|
||||
for interval in intervals {
|
||||
match interval.interval {
|
||||
FrameIntervalEnum::Discrete(dis) => {
|
||||
compatible_fps.push(dis.denominator);
|
||||
}
|
||||
FrameIntervalEnum::Stepwise(step) => {
|
||||
compatible_fps.push(step.min.denominator);
|
||||
compatible_fps.push(step.max.denominator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::CouldntQueryDevice {
|
||||
property: "Framerate".to_string(),
|
||||
error: why.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
resmap.insert(res, compatible_fps);
|
||||
}
|
||||
Ok(resmap)
|
||||
}
|
||||
|
||||
fn get_compatible_list_by_resolution(&self, fourcc: FrameFormat) -> std::collections::HashMap<Resolution, Vec<u32>> {
|
||||
todo!()
|
||||
}
|
||||
fn get_resolution_list(&self, fourcc: FrameFormat) -> Result<Vec<Resolution>, NokhwaError> {
|
||||
let format = match fourcc {
|
||||
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
|
||||
FrameFormat::YUYV => FourCC::new(b"YUYV"),
|
||||
};
|
||||
|
||||
fn get_compatible_fps_by_resolution(&self, res: Resolution, fourcc: FrameFormat) -> Vec<u32> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_compatible_res_by_framerate(&self, fps: u32, fourcc: FrameFormat) -> Vec<Resolution> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_compatible_fourcc(&self, resolution: Resolution, framerate: u32) -> Vec<FrameFormat> {
|
||||
todo!()
|
||||
match self.device.enum_framesizes(format) {
|
||||
Ok(framesizes) => {
|
||||
let mut resolutions = vec![];
|
||||
for framesize in framesizes {
|
||||
match framesize.size {
|
||||
FrameSizeEnum::Discrete(dis) => {
|
||||
resolutions.push(Resolution::new(dis.width, dis.height))
|
||||
}
|
||||
FrameSizeEnum::Stepwise(step) => {
|
||||
resolutions.push(Resolution::new(step.min_width, step.min_height));
|
||||
resolutions.push(Resolution::new(step.max_width, step.max_height));
|
||||
// TODO: Respect step size
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(resolutions)
|
||||
}
|
||||
Err(why) => Err(NokhwaError::CouldntQueryDevice {
|
||||
property: "Resolutions".to_string(),
|
||||
error: why.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,16 +70,17 @@ pub trait CaptureBackendTrait {
|
||||
|
||||
/// This is for any backend that allows you to query a camera for its compatible resolutions/fourcc/framerates.
|
||||
pub trait QueryBackendTrait: CaptureBackendTrait {
|
||||
/// A hashmap of Framerates mapped to [`Resolution`]s.
|
||||
fn get_compatible_list_by_framerate(&self, fourcc: FrameFormat) -> HashMap<u32, Vec<Resolution>>;
|
||||
/// A hashmap of [`Resolution`]s mapped to framerates
|
||||
fn get_compatible_list_by_resolution(&self, fourcc: FrameFormat) -> HashMap<Resolution, Vec<u32>>;
|
||||
///
|
||||
fn get_compatible_fps_by_resolution(&self, res: Resolution, fourcc: FrameFormat) -> Vec<u32>;
|
||||
///
|
||||
fn get_compatible_res_by_framerate(&self, fps: u32, fourcc: FrameFormat) -> Vec<Resolution>;
|
||||
/// # Errors
|
||||
/// This will error if the camera is not queryable or a query operation has failed.
|
||||
fn get_compatible_list_by_resolution(
|
||||
&self,
|
||||
fourcc: FrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError>;
|
||||
/// Gets the supported camera formats.
|
||||
fn get_compatible_fourcc(&self, resolution: Resolution, framerate: u32) -> Vec<FrameFormat>;
|
||||
/// # Errors
|
||||
/// This will error if the camera is not queryable or a query operation has failed.
|
||||
fn get_resolution_list(&self, fourcc: FrameFormat) -> Result<Vec<Resolution>, NokhwaError>;
|
||||
}
|
||||
|
||||
pub trait VirtualBackendTrait {}
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::FrameFormat;
|
||||
use crate::{CaptureAPIBackend, FrameFormat};
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[allow(clippy::pub_enum_variant_names)]
|
||||
@@ -26,4 +26,6 @@ pub enum NokhwaError {
|
||||
destination: String,
|
||||
error: String,
|
||||
},
|
||||
#[error("This operation is not supported by backend {0}.")]
|
||||
UnsupportedOperation(CaptureAPIBackend),
|
||||
}
|
||||
|
||||
+4
-4
@@ -247,7 +247,7 @@ impl Ord for CameraInfo {
|
||||
/// - AUTO is special - it tells the Camera struct to automatically choose a backend most suited for the current platform.
|
||||
/// - V4L2 - `Video4Linux2`, a linux specific backend.
|
||||
/// - UVC - Universal Video Class (please check [libuvc](https://github.com/libuvc/libuvc)). Platform agnostic, although on linux it needs `sudo` permissions or similar to use.
|
||||
/// - MSMF - Microsoft Media Foundation, Winsows only (replacement for `DirectShow`)
|
||||
/// - Windows - Directshow, Windows only
|
||||
/// - `OpenCV` - Uses `OpenCV` to capture. Platform agnostic.
|
||||
/// - FFMPEG - Uses FFMPEG (libavdevice) to capture. Platform agnostic.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -255,7 +255,7 @@ pub enum CaptureAPIBackend {
|
||||
AUTO,
|
||||
V4L2,
|
||||
UVC,
|
||||
MSMF,
|
||||
WINDOWS,
|
||||
OPENCV,
|
||||
FFMPEG,
|
||||
}
|
||||
@@ -310,7 +310,7 @@ pub fn mjpeg_to_rgb888(data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
// The YUY2(YUYV) format is a 16 bit format. We read 4 bytes at a time to get 6 bytes of RGB888.
|
||||
// First, the YUY2 is converted to YCbCr 4:4:4 (4:2:2 -> 4:4:4)
|
||||
// then it is converted to 6 bytes (2 pixels) of RGB888
|
||||
/// Converts a YUYV 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
|
||||
/// Converts a YUYV 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
|
||||
/// # Errors
|
||||
/// This may error when the data stream size is not divisible by 4, a i32 -> u8 conversion fails, or it fails to read from a certain index.
|
||||
pub fn yuyv422_to_rgb888(data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
@@ -410,7 +410,7 @@ pub fn yuyv422_to_rgb888(data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
}
|
||||
|
||||
// equation from https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB
|
||||
/// Convert `YCbCr` 4:4:4 to a RGB888. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
|
||||
/// Convert `YCbCr` 4:4:4 to a RGB888. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB)
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
|
||||
Reference in New Issue
Block a user