add uvc, adjust traits

This commit is contained in:
l1npengtul
2021-05-24 17:30:29 +09:00
parent 4549b443c4
commit c2646e727e
9 changed files with 174 additions and 48 deletions
+20
View File
@@ -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
View File
@@ -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"]
-3
View File
@@ -1,3 +0,0 @@
fn main() {
// TODO: add windows bindgen
}
+2
View File
@@ -1,2 +1,4 @@
#[cfg(feature = "input_v4l")]
pub mod v4l2;
#[cfg(feature = "input_uvc")]
pub mod uvc;
+52
View File
@@ -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())),
};
}
}
+80 -22
View File
@@ -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(),
}),
}
}
}
+9 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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)]