mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
add finishing touches for 0.3.0 release
This commit is contained in:
@@ -32,7 +32,7 @@ loop {
|
|||||||
println!("{}, {}", frame.width(), frame.height());
|
println!("{}, {}", frame.width(), frame.height());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
They can be found in the `examples` folder.
|
A command line app made with `nokhwa` can be found in the `examples` folder.
|
||||||
|
|
||||||
## API Support
|
## API Support
|
||||||
The table below lists current Nokhwa API support.
|
The table below lists current Nokhwa API support.
|
||||||
@@ -54,7 +54,7 @@ The table below lists current Nokhwa API support.
|
|||||||
| MSMF | * | * | * | Windows |
|
| MSMF | * | * | * | Windows |
|
||||||
| JS/WASM | * | * | * | Web |
|
| JS/WASM | * | * | * | Web |
|
||||||
|
|
||||||
:white_check_mark: : Working, :warning: : Experimental, :x: : Not Supported, *: Planned
|
:white_check_mark: : Working, :warning: : Experimental, :x: : Not Supported, *: Planned/WIP
|
||||||
|
|
||||||
^ = No CameraFormat setting support.
|
^ = No CameraFormat setting support.
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
- run on windows
|
- run on windows
|
||||||
- ensure updated documentation by running `cargo doc` and reading the README
|
- ensure updated documentation by running `cargo doc` and reading the README
|
||||||
- update the version number in Cargo.toml
|
- update the version number in Cargo.toml
|
||||||
|
- update changelog
|
||||||
- do this one more time
|
- do this one more time
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
## capture
|
||||||
|
Capture is a command line application designed to test features of backends to see if they are implemented correctly.
|
||||||
|
For the UVC backend, you may need to run the app as admin.
|
||||||
|
|
||||||
|
### Capture - Usage
|
||||||
|
`<>` indicates an optional parameter. `[]` indicates a mandatory one.
|
||||||
|
- `-q <BACKEND>`/`--query <BACKEND>`: Queries the system for available devices. If `<BACKEND>` is set, it will query using that backend.
|
||||||
|
- `-c <DEVICE>`/`--capture <DEVICE>`: Captures using device. If `<DEVICE>` is set, it will using that device index or IP.
|
||||||
|
- `-s`/`--query-device`: Show device queries from `compatible_fourcc` and `compatible_list_by_resolution`. Requires -c to be passed to work.
|
||||||
|
- `-w [WIDTH:U32]`/`--width [WIDTH:U32]`: Set width of capture to `[WIDTH:U32]`. Does nothing if -c flag is not set. Value Has to be a `u32`
|
||||||
|
- `-h [HEIGHT:U32]`/`--height [HEIGHT:U32]`: Set height of capture to `[HEIGHT:U32]`. Does nothing if -c flag is not set. Value Has to be a `u32`
|
||||||
|
- `-rate [FPS:U32]`/`--framerate [FPS:U32]`: Set FPS of capture to `[FPS:U32]`. Does nothing if -c flag is not set. Value Has to be a `u32`
|
||||||
|
- `-4cc [FORMAT]`/`--format [FORMAT]`: Set format of capture to `[FORMAT]`. Does nothing if -c flag is not set. Possible values are MJPG and YUYV. Will be ignored if not one of those two.
|
||||||
|
- `-b [BACKEND]`/`--backend [BACKEND]`: Set the capture backend to `[BACKEND]`. Pass AUTO for automatic backend, UVC to use UVC, V4L to use Video4Linux, GST to use Gstreamer, OPENCV to use OpenCV.
|
||||||
|
- `-d`/`--display`: Enable glium display. Note: This is currently bugged as it shows an upside down feed. It also does not respond to `x` button press from window. (FIXME)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
```
|
||||||
|
./capture -q V4L -c 0 -s -w 1920 -h 1080 --framerate 30 --format MJPG -b V4L -d
|
||||||
|
```
|
||||||
|
Query system using V4L backend, capture device with index 0 at 1920x1080 @ 30 FPS on MJPG format, using backend V4L, then display to glium window.
|
||||||
@@ -4,7 +4,7 @@ use glium::{
|
|||||||
IndexBuffer, Surface, Texture2d, VertexBuffer,
|
IndexBuffer, Surface, Texture2d, VertexBuffer,
|
||||||
};
|
};
|
||||||
use glutin::{event_loop::EventLoop, window::WindowBuilder, ContextBuilder};
|
use glutin::{event_loop::EventLoop, window::WindowBuilder, ContextBuilder};
|
||||||
use nokhwa::{query_devices, Camera, CaptureAPIBackend, FrameFormat, NetworkCamera, Resolution};
|
use nokhwa::{query_devices, Camera, CaptureAPIBackend, FrameFormat, NetworkCamera};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@@ -60,7 +60,7 @@ fn main() {
|
|||||||
.default_value("15")
|
.default_value("15")
|
||||||
.takes_value(true))
|
.takes_value(true))
|
||||||
.arg(Arg::with_name("format")
|
.arg(Arg::with_name("format")
|
||||||
.short("l")
|
.short("4cc")
|
||||||
.long("format")
|
.long("format")
|
||||||
.value_name("FORMAT")
|
.value_name("FORMAT")
|
||||||
.help("Set format of capture. Does nothing if -c flag is not set. Possible values are MJPG and YUYV. Will be ignored if not either. Ignored by GStreamer backend.")
|
.help("Set format of capture. Does nothing if -c flag is not set. Possible values are MJPG and YUYV. Will be ignored if not either. Ignored by GStreamer backend.")
|
||||||
|
|||||||
@@ -392,17 +392,17 @@ impl CaptureBackendTrait for GStreamerCaptureDevice {
|
|||||||
self.camera_format.framerate()
|
self.camera_format.framerate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||||
let mut new_fmt = self.camera_format;
|
let mut new_fmt = self.camera_format;
|
||||||
new_fmt.set_framerate(new_fps);
|
new_fmt.set_framerate(new_fps);
|
||||||
self.set_camera_format(new_fmt)
|
self.set_camera_format(new_fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frameformat(&self) -> FrameFormat {
|
fn frame_format(&self) -> FrameFormat {
|
||||||
self.camera_format.format()
|
self.camera_format.format()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_frameformat(&mut self, _fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
fn set_frame_format(&mut self, _fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||||
Err(NokhwaError::UnsupportedOperation(
|
Err(NokhwaError::UnsupportedOperation(
|
||||||
CaptureAPIBackend::GStreamer,
|
CaptureAPIBackend::GStreamer,
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,20 +1,38 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
tryinto_num, vector, CameraFormat, CameraInfo, CaptureAPIBackend, CaptureBackendTrait,
|
CameraFormat, CameraIndexType, CameraInfo, CaptureAPIBackend, CaptureBackendTrait, FrameFormat,
|
||||||
FrameFormat, NokhwaError, Resolution,
|
NokhwaError, Resolution,
|
||||||
};
|
};
|
||||||
use image::{ImageBuffer, Rgb};
|
use image::{ImageBuffer, Rgb};
|
||||||
use opencv::core::Vector;
|
|
||||||
use opencv::{
|
use opencv::{
|
||||||
core::{Mat, MatTrait, MatTraitManual, Vec3b},
|
core::{Mat, MatTrait, MatTraitManual, Vec3b},
|
||||||
videoio::{
|
videoio::{
|
||||||
VideoCapture, VideoCaptureTrait, VideoWriter, CAP_ANY, CAP_AVFOUNDATION, CAP_DSHOW,
|
VideoCapture, VideoCaptureTrait, CAP_ANY, CAP_AVFOUNDATION, CAP_MSMF, CAP_PROP_FPS,
|
||||||
CAP_PROP_FOURCC, CAP_PROP_FPS, CAP_PROP_FRAME_HEIGHT, CAP_PROP_FRAME_WIDTH, CAP_V4L2,
|
CAP_PROP_FRAME_HEIGHT, CAP_PROP_FRAME_WIDTH, CAP_V4L2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::collections::HashMap;
|
||||||
collections::HashMap,
|
|
||||||
fmt::{Display, Formatter},
|
/// Converts $from into $to
|
||||||
};
|
/// Example usage:
|
||||||
|
/// `tryinto_num(i32, a_unsigned_32_bit_num)`
|
||||||
|
/// Designed to deal with infallible. If not, it should be manually handled.
|
||||||
|
/// # Errors
|
||||||
|
/// If fails to convert(note: should not happen) then you messed up.
|
||||||
|
macro_rules! tryinto_num {
|
||||||
|
($to:ty, $from:expr) => {{
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
match <$to>::try_from($from) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(why) => {
|
||||||
|
return Err(crate::NokhwaError::GeneralError(format!(
|
||||||
|
"Failed to convert {}, {}",
|
||||||
|
$from,
|
||||||
|
why.to_string()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Define behaviour for IPCameras.
|
// TODO: Define behaviour for IPCameras.
|
||||||
/// The backend struct that interfaces with `OpenCV`. Note that an `opencv` matching the version that this was either compiled on must be present on the user's machine. (usually 4.5.2 or greater)
|
/// The backend struct that interfaces with `OpenCV`. Note that an `opencv` matching the version that this was either compiled on must be present on the user's machine. (usually 4.5.2 or greater)
|
||||||
@@ -22,17 +40,17 @@ use std::{
|
|||||||
///
|
///
|
||||||
/// To see what this does, please see [`CaptureBackendTrait`]
|
/// To see what this does, please see [`CaptureBackendTrait`]
|
||||||
/// # Quirks
|
/// # Quirks
|
||||||
/// - **Some features don't work properly on this backend (yet)! Setting Resolution, FPS, FourCC does not work and will default to 640x480 30FPS. This is being worked on.**
|
/// - **Some features don't work properly on this backend (yet)! Setting [`Resolution`], FPS, [`FrameFormat`] does not work and will default to 640x480 30FPS. This is being worked on.**
|
||||||
/// - This is a **cross-platform** backend. This means that it will work on most platforms given that `OpenCV` is present.
|
/// - This is a **cross-platform** backend. This means that it will work on most platforms given that `OpenCV` is present.
|
||||||
/// - This backend can also do IP Camera input.
|
/// - This backend can also do IP Camera input.
|
||||||
/// - The backend's backend will default to system level APIs on Linux(V4L2), Mac(AVFoundation), and Windows(Media Foundation). Otherwise, it will decide for itself.
|
/// - The backend's backend will default to system level APIs on Linux(V4L2), Mac(AVFoundation), and Windows(Media Foundation). Otherwise, it will decide for itself.
|
||||||
/// - If the [`OpenCvCaptureDevice`] is initialized as a `IPCamera`, the [`CameraFormat`]'s `index` value will be [`u32::MAX`](std::u32::MAX) (4294967295).
|
/// - If the [`OpenCvCaptureDevice`] is initialized as a `IPCamera`, the [`CameraFormat`]'s `index` value will be [`u32::MAX`](std::u32::MAX) (4294967295).
|
||||||
/// - `OpenCV` does not support camera querying. Camera Name and Camera supported resolution/fps/fourcc is a [`UnsupportedOperation`](NokhwaError::UnsupportedOperation).
|
/// - `OpenCV` does not support camera querying. Camera Name and Camera supported resolution/fps/fourcc is a [`UnsupportedOperation`](NokhwaError::UnsupportedOperation).
|
||||||
/// Note: [`get_resolution()`](CaptureBackendTrait::get_resolution()), [`get_frameformat()`](CaptureBackendTrait::get_frameformat()), and [`get_framerate()`](CaptureBackendTrait::get_framerate()) is not affected.
|
/// Note: [`resolution()`](CaptureBackendTrait::resolution()), [`frame_format()`](CaptureBackendTrait::frame_format()), and [`frame_rate()`](CaptureBackendTrait::frame_rate()) is not affected.
|
||||||
/// - [`CameraInfo`]'s human name will be "`OpenCV` Capture Device {location}"
|
/// - [`CameraInfo`]'s human name will be "`OpenCV` Capture Device {location}"
|
||||||
/// - [`CameraInfo`]'s description will contain the Camera's Index or IP.
|
/// - [`CameraInfo`]'s description will contain the Camera's Index or IP.
|
||||||
/// - [`get_frame_raw()`](CaptureBackendTrait::get_frame_raw()) returns a BGR24 image instead of \<native format>.
|
/// - [`frame_raw()`](CaptureBackendTrait::frame_raw()) returns a BGR24 image instead of \<native format>.
|
||||||
/// - The API Preference order is the native OS API (linux => `v4l2`, mac => `AVFoundation`, windows => `directshow`) than [`CAP_AUTO`](https://docs.opencv.org/4.5.2/d4/d15/group__videoio__flags__base.html#gga023786be1ee68a9105bf2e48c700294da77ab1fe260fd182f8ec7655fab27a31d)
|
/// - The API Preference order is the native OS API (linux => `v4l2`, mac => `AVFoundation`, windows => `MSMF`) than [`CAP_AUTO`](https://docs.opencv.org/4.5.2/d4/d15/group__videoio__flags__base.html#gga023786be1ee68a9105bf2e48c700294da77ab1fe260fd182f8ec7655fab27a31d)
|
||||||
pub struct OpenCvCaptureDevice {
|
pub struct OpenCvCaptureDevice {
|
||||||
camera_format: CameraFormat,
|
camera_format: CameraFormat,
|
||||||
camera_location: CameraIndexType,
|
camera_location: CameraIndexType,
|
||||||
@@ -223,13 +241,8 @@ impl OpenCvCaptureDevice {
|
|||||||
return match frame.is_continuous() {
|
return match frame.is_continuous() {
|
||||||
Ok(cont) => {
|
Ok(cont) => {
|
||||||
if cont {
|
if cont {
|
||||||
|
println!("{:?}", frame);
|
||||||
let mut raw_vec: Vec<u8> = Vec::new();
|
let mut raw_vec: Vec<u8> = Vec::new();
|
||||||
raw_vec.reserve(
|
|
||||||
(self.resolution().width()
|
|
||||||
* self.resolution().height()
|
|
||||||
* (frame.channels().unwrap_or(3)) as u32)
|
|
||||||
as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
let frame_data_vec = match Mat::data_typed::<Vec3b>(&frame) {
|
let frame_data_vec = match Mat::data_typed::<Vec3b>(&frame) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@@ -307,7 +320,7 @@ impl OpenCvCaptureDevice {
|
|||||||
/// If the framerate is failed to be read (e.g. invalid or not supported), this will error.
|
/// If the framerate is failed to be read (e.g. invalid or not supported), this will error.
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
pub fn get_framerate_raw(&self) -> Result<u32, NokhwaError> {
|
pub fn raw_framerate(&self) -> Result<u32, NokhwaError> {
|
||||||
match self.video_capture.get(CAP_PROP_FPS) {
|
match self.video_capture.get(CAP_PROP_FPS) {
|
||||||
Ok(fps) => Ok(fps as u32),
|
Ok(fps) => Ok(fps as u32),
|
||||||
Err(why) => Err(NokhwaError::CouldntQueryDevice {
|
Err(why) => Err(NokhwaError::CouldntQueryDevice {
|
||||||
@@ -366,7 +379,8 @@ impl CaptureBackendTrait for OpenCvCaptureDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolution(&self) -> Resolution {
|
fn resolution(&self) -> Resolution {
|
||||||
self.camera_format.resolution()
|
self.raw_resolution()
|
||||||
|
.unwrap_or_else(|_| Resolution::new(640, 480))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
|
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
|
||||||
@@ -376,20 +390,20 @@ impl CaptureBackendTrait for OpenCvCaptureDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn frame_rate(&self) -> u32 {
|
fn frame_rate(&self) -> u32 {
|
||||||
self.camera_format.framerate()
|
self.raw_framerate().unwrap_or(30)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||||
let mut current_fmt = self.camera_format;
|
let mut current_fmt = self.camera_format;
|
||||||
current_fmt.set_framerate(new_fps);
|
current_fmt.set_framerate(new_fps);
|
||||||
self.set_camera_format(current_fmt)
|
self.set_camera_format(current_fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frameformat(&self) -> FrameFormat {
|
fn frame_format(&self) -> FrameFormat {
|
||||||
self.camera_format.format()
|
self.camera_format.format()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||||
let mut current_fmt = self.camera_format;
|
let mut current_fmt = self.camera_format;
|
||||||
current_fmt.set_format(fourcc);
|
current_fmt.set_format(fourcc);
|
||||||
self.set_camera_format(current_fmt)
|
self.set_camera_format(current_fmt)
|
||||||
@@ -487,121 +501,23 @@ impl CaptureBackendTrait for OpenCvCaptureDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `OpenCV` backend supports both native cameras and IP Cameras, so this is an enum to differentiate them
|
|
||||||
/// The `IPCamera`'s string follows the pattern
|
|
||||||
/// ```.ignore
|
|
||||||
/// <protocol>://<IP>:<port>/
|
|
||||||
/// ```
|
|
||||||
/// but please consult the manufacturer's specification for more details.
|
|
||||||
/// The index is a standard webcam index.
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum CameraIndexType {
|
|
||||||
Index(u32),
|
|
||||||
IPCamera(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for CameraIndexType {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
CameraIndexType::Index(idx) => {
|
|
||||||
write!(f, "{}", idx)
|
|
||||||
}
|
|
||||||
CameraIndexType::IPCamera(ip) => {
|
|
||||||
write!(f, "{}", ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_api_pref_int() -> u32 {
|
fn get_api_pref_int() -> u32 {
|
||||||
match std::env::consts::OS {
|
match std::env::consts::OS {
|
||||||
"linux" => CAP_V4L2 as u32,
|
"linux" => CAP_V4L2 as u32,
|
||||||
"windows" => CAP_DSHOW as u32,
|
"windows" => CAP_MSMF as u32,
|
||||||
"mac" => CAP_AVFOUNDATION as u32,
|
"mac" => CAP_AVFOUNDATION as u32,
|
||||||
&_ => CAP_ANY as u32,
|
&_ => CAP_ANY as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
|
// I'm done. This stupid POS refuses to actually do anything useful with camera settings
|
||||||
|
// If anyone else wants to tackle this monster, please do.
|
||||||
fn set_properties(
|
fn set_properties(
|
||||||
vc: &mut VideoCapture,
|
_vc: &mut VideoCapture,
|
||||||
camera_format: CameraFormat,
|
_camera_format: CameraFormat,
|
||||||
camera_location: &CameraIndexType,
|
_camera_location: &CameraIndexType,
|
||||||
) -> Result<(), NokhwaError> {
|
) -> Result<(), NokhwaError> {
|
||||||
let fourcc = match camera_format.format() {
|
|
||||||
FrameFormat::MJPEG => {
|
|
||||||
match VideoWriter::fourcc('m' as i8, 'j' as i8, 'p' as i8, 'g' as i8) {
|
|
||||||
Ok(fmt) => fmt,
|
|
||||||
Err(why) => {
|
|
||||||
return Err(NokhwaError::CouldntSetProperty {
|
|
||||||
property: "FrameFormat".to_string(),
|
|
||||||
value: "FourCC MJPG".to_string(),
|
|
||||||
error: why.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FrameFormat::YUYV => {
|
|
||||||
match VideoWriter::fourcc('y' as i8, 'u' as i8, 'y' as i8, 'v' as i8) {
|
|
||||||
Ok(fmt) => fmt,
|
|
||||||
Err(why) => {
|
|
||||||
return Err(NokhwaError::CouldntSetProperty {
|
|
||||||
property: "FrameFormat".to_string(),
|
|
||||||
value: "FourCC YUYV".to_string(),
|
|
||||||
error: why.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let properties: &Vector<i32> = &vector!(
|
|
||||||
CAP_PROP_FOURCC as i32,
|
|
||||||
fourcc,
|
|
||||||
CAP_PROP_FRAME_WIDTH as i32,
|
|
||||||
camera_format.width() as i32,
|
|
||||||
CAP_PROP_FRAME_HEIGHT as i32,
|
|
||||||
camera_format.height() as i32,
|
|
||||||
CAP_PROP_FPS as i32,
|
|
||||||
camera_format.framerate() as i32
|
|
||||||
);
|
|
||||||
|
|
||||||
match camera_location {
|
|
||||||
CameraIndexType::Index(idx) => {
|
|
||||||
match vc.open_2(*idx as i32, get_api_pref_int() as i32, properties) {
|
|
||||||
Ok(v) => {
|
|
||||||
if !v {
|
|
||||||
return Err(NokhwaError::CouldntOpenDevice(
|
|
||||||
"Failed to re-open camera, OpenCV Bool return error".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(why) => {
|
|
||||||
return Err(NokhwaError::CouldntOpenDevice(format!(
|
|
||||||
"Failed to re-open camera with properties: {}",
|
|
||||||
why.to_string()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CameraIndexType::IPCamera(ip) => {
|
|
||||||
match vc.open(ip.as_str(), get_api_pref_int() as i32, properties) {
|
|
||||||
Ok(v) => {
|
|
||||||
if !v {
|
|
||||||
return Err(NokhwaError::CouldntOpenDevice(
|
|
||||||
"Failed to re-open camera, OpenCV Bool return error".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(why) => {
|
|
||||||
return Err(NokhwaError::CouldntOpenDevice(format!(
|
|
||||||
"Failed to re-open camera with properties: {}",
|
|
||||||
why.to_string()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,17 +297,17 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
|
|||||||
self.borrow_camera_format().framerate()
|
self.borrow_camera_format().framerate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||||
let mut current_format = *self.borrow_camera_format();
|
let mut current_format = *self.borrow_camera_format();
|
||||||
current_format.set_framerate(new_fps);
|
current_format.set_framerate(new_fps);
|
||||||
self.set_camera_format(current_format)
|
self.set_camera_format(current_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frameformat(&self) -> FrameFormat {
|
fn frame_format(&self) -> FrameFormat {
|
||||||
self.borrow_camera_format().format()
|
self.borrow_camera_format().format()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||||
let mut current_format = *self.borrow_camera_format();
|
let mut current_format = *self.borrow_camera_format();
|
||||||
current_format.set_format(fourcc);
|
current_format.set_format(fourcc);
|
||||||
self.set_camera_format(current_format)
|
self.set_camera_format(current_format)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ impl From<CameraFormat> for Format {
|
|||||||
/// The backend struct that interfaces with V4L2.
|
/// The backend struct that interfaces with V4L2.
|
||||||
/// To see what this does, please see [`CaptureBackendTrait`].
|
/// To see what this does, please see [`CaptureBackendTrait`].
|
||||||
/// # Quirks
|
/// # Quirks
|
||||||
/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
|
/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_frame_rate()`](CaptureBackendTrait::set_frame_rate), or [`set_frame_format()`](CaptureBackendTrait::set_frame_format) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
|
||||||
pub struct V4LCaptureDevice<'a> {
|
pub struct V4LCaptureDevice<'a> {
|
||||||
camera_format: CameraFormat,
|
camera_format: CameraFormat,
|
||||||
camera_info: CameraInfo,
|
camera_info: CameraInfo,
|
||||||
@@ -344,18 +344,18 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::option_if_let_else)]
|
#[allow(clippy::option_if_let_else)]
|
||||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||||
let mut new_fmt = self.camera_format;
|
let mut new_fmt = self.camera_format;
|
||||||
new_fmt.set_framerate(new_fps);
|
new_fmt.set_framerate(new_fps);
|
||||||
self.set_camera_format(new_fmt)
|
self.set_camera_format(new_fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frameformat(&self) -> FrameFormat {
|
fn frame_format(&self) -> FrameFormat {
|
||||||
self.camera_format.format()
|
self.camera_format.format()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::option_if_let_else)]
|
#[allow(clippy::option_if_let_else)]
|
||||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||||
let mut new_fmt = self.camera_format;
|
let mut new_fmt = self.camera_format;
|
||||||
new_fmt.set_format(fourcc);
|
new_fmt.set_format(fourcc);
|
||||||
self.set_camera_format(new_fmt)
|
self.set_camera_format(new_fmt)
|
||||||
|
|||||||
+114
-10
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
cap_impl_fn, cap_impl_matches, CameraFormat, CameraInfo, CaptureAPIBackend,
|
CameraFormat, CameraInfo, CaptureAPIBackend, CaptureBackendTrait, FrameFormat, NokhwaError,
|
||||||
CaptureBackendTrait, FrameFormat, NokhwaError, Resolution,
|
Resolution,
|
||||||
};
|
};
|
||||||
use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbaImage};
|
use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbaImage};
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
@@ -129,28 +129,28 @@ impl Camera {
|
|||||||
self.backend.borrow_mut().set_resolution(new_res)
|
self.backend.borrow_mut().set_resolution(new_res)
|
||||||
}
|
}
|
||||||
/// Gets the current camera framerate (See: [`CameraFormat`]).
|
/// Gets the current camera framerate (See: [`CameraFormat`]).
|
||||||
pub fn framerate(&self) -> u32 {
|
pub fn frame_rate(&self) -> u32 {
|
||||||
self.backend.borrow().frame_rate()
|
self.backend.borrow().frame_rate()
|
||||||
}
|
}
|
||||||
/// Will set the current framerate
|
/// Will set the current framerate
|
||||||
/// This will reset the current stream if used while stream is opened.
|
/// This will reset the current stream if used while stream is opened.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If you started the stream and the camera rejects the new framerate, this will return an error.
|
/// If you started the stream and the camera rejects the new framerate, this will return an error.
|
||||||
pub fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
pub fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||||
self.backend.borrow_mut().set_framerate(new_fps)
|
self.backend.borrow_mut().set_frame_rate(new_fps)
|
||||||
}
|
}
|
||||||
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
|
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
|
||||||
pub fn frameformat(&self) -> FrameFormat {
|
pub fn frame_format(&self) -> FrameFormat {
|
||||||
self.backend.borrow().frameformat()
|
self.backend.borrow().frame_format()
|
||||||
}
|
}
|
||||||
/// Will set the current [`FrameFormat`]
|
/// Will set the current [`FrameFormat`]
|
||||||
/// This will reset the current stream if used while stream is opened.
|
/// This will reset the current stream if used while stream is opened.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If you started the stream and the camera rejects the new frame format, this will return an error.
|
/// If you started the stream and the camera rejects the new frame format, this will return an error.
|
||||||
pub fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
pub fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||||
self.backend.borrow_mut().set_frameformat(fourcc)
|
self.backend.borrow_mut().set_frame_format(fourcc)
|
||||||
}
|
}
|
||||||
/// Will open the camera stream with set parameters. This will be called internally if you try and call [`get_frame()`](CaptureBackendTrait::get_frame()) before you call [`open_stream()`](CaptureBackendTrait::open_stream()).
|
/// Will open the camera stream with set parameters. This will be called internally if you try and call [`frame()`](CaptureBackendTrait::frame()) before you call [`open_stream()`](CaptureBackendTrait::open_stream()).
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If the specific backend fails to open the camera (e.g. already taken, busy, doesn't exist anymore) this will error.
|
/// If the specific backend fails to open the camera (e.g. already taken, busy, doesn't exist anymore) this will error.
|
||||||
pub fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
pub fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||||
@@ -297,6 +297,110 @@ fn figure_out_auto() -> Option<CaptureAPIBackend> {
|
|||||||
Some(cap)
|
Some(cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! cap_impl_fn {
|
||||||
|
{
|
||||||
|
$( ($backend:ty, $init_fn:ident, $feature:expr, $backend_name:ident) ),+
|
||||||
|
} => {
|
||||||
|
$(
|
||||||
|
paste::paste! {
|
||||||
|
#[cfg(feature = $feature)]
|
||||||
|
fn [< init_ $backend_name>](idx: usize, setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
|
||||||
|
use crate::backends::capture::$backend;
|
||||||
|
match <$backend>::$init_fn(idx, setting) {
|
||||||
|
Ok(cap) => Some(Ok(Box::new(cap))),
|
||||||
|
Err(why) => Some(Err(why)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = $feature))]
|
||||||
|
fn [< init_ $backend_name>](_idx: usize, _setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cap_impl_matches {
|
||||||
|
{
|
||||||
|
$use_backend: expr, $index:expr, $setting:expr,
|
||||||
|
$( ($feature:expr, $backend:ident, $fn:ident) ),+
|
||||||
|
} => {
|
||||||
|
{
|
||||||
|
let i = $index;
|
||||||
|
let s = $setting;
|
||||||
|
match $use_backend {
|
||||||
|
CaptureAPIBackend::Auto => match figure_out_auto() {
|
||||||
|
Some(cap) => match cap {
|
||||||
|
$(
|
||||||
|
CaptureAPIBackend::$backend => {
|
||||||
|
match cfg!(feature = $feature) {
|
||||||
|
true => {
|
||||||
|
match $fn(i,s) {
|
||||||
|
Some(cap) => match cap {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(why) => return Err(why),
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
_ => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
CaptureAPIBackend::$backend => {
|
||||||
|
match cfg!(feature = $feature) {
|
||||||
|
true => {
|
||||||
|
match $fn(i,s) {
|
||||||
|
Some(cap) => match cap {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(why) => return Err(why),
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
_ => {
|
||||||
|
return Err(NokhwaError::NotImplemented(
|
||||||
|
"Platform requirements not satisfied.".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cap_impl_fn! {
|
cap_impl_fn! {
|
||||||
(GStreamerCaptureDevice, new, "input-gst", gst),
|
(GStreamerCaptureDevice, new, "input-gst", gst),
|
||||||
(OpenCvCaptureDevice, new_autopref, "input-opencv", opencv),
|
(OpenCvCaptureDevice, new_autopref, "input-opencv", opencv),
|
||||||
|
|||||||
@@ -60,18 +60,18 @@ pub trait CaptureBackendTrait {
|
|||||||
/// This will reset the current stream if used while stream is opened.
|
/// This will reset the current stream if used while stream is opened.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If you started the stream and the camera rejects the new framerate, this will return an error.
|
/// If you started the stream and the camera rejects the new framerate, this will return an error.
|
||||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError>;
|
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError>;
|
||||||
|
|
||||||
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
|
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
|
||||||
fn frameformat(&self) -> FrameFormat;
|
fn frame_format(&self) -> FrameFormat;
|
||||||
|
|
||||||
/// Will set the current [`FrameFormat`]
|
/// Will set the current [`FrameFormat`]
|
||||||
/// This will reset the current stream if used while stream is opened.
|
/// This will reset the current stream if used while stream is opened.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If you started the stream and the camera rejects the new frame format, this will return an error.
|
/// If you started the stream and the camera rejects the new frame format, this will return an error.
|
||||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError>;
|
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError>;
|
||||||
|
|
||||||
/// Will open the camera stream with set parameters. This will be called internally if you try and call [`get_frame()`](CaptureBackendTrait::get_frame()) before you call [`open_stream()`](CaptureBackendTrait::open_stream()).
|
/// Will open the camera stream with set parameters. This will be called internally if you try and call [`frame()`](CaptureBackendTrait::frame()) before you call [`open_stream()`](CaptureBackendTrait::open_stream()).
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If the specific backend fails to open the camera (e.g. already taken, busy, doesn't exist anymore) this will error.
|
/// If the specific backend fails to open the camera (e.g. already taken, busy, doesn't exist anymore) this will error.
|
||||||
fn open_stream(&mut self) -> Result<(), NokhwaError>;
|
fn open_stream(&mut self) -> Result<(), NokhwaError>;
|
||||||
|
|||||||
+78
-80
@@ -1,85 +1,85 @@
|
|||||||
|
//! # nokhwa
|
||||||
|
//! Nokhwa(녹화): Korean word meaning "to record".
|
||||||
|
//!
|
||||||
|
//! A Simple-to-use, cross-platform Rust Webcam Capture Library
|
||||||
|
//!
|
||||||
|
//! ## Using nokhwa
|
||||||
|
//! You will need the latest stable Rust and Cargo.
|
||||||
|
//!
|
||||||
|
//! Nokhwa can be added to your crate by adding it to your `Cargo.toml`:
|
||||||
|
//! ```.ignore
|
||||||
|
//! [dependencies.nokhwa]
|
||||||
|
//! // TODO: replace the "*" with the latest version of `nokhwa`
|
||||||
|
//! version = "*"
|
||||||
|
//! // TODO: add some features
|
||||||
|
//! features = [""]
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Most likely, you will only use functionality provided by the `Camera` struct. If you need lower-level access, you may instead opt to use the raw capture backends found at `nokhwa::backends::capture::*`.
|
||||||
|
//! ## API Support
|
||||||
|
//! The table below lists current Nokhwa API support.
|
||||||
|
//! - The `Backend` column signifies the backend.
|
||||||
|
//! - The `Input` column signifies reading frames from the camera
|
||||||
|
//! - The `Query` column signifies system device list support
|
||||||
|
//! - The `Query-Device` column signifies reading device capabilities
|
||||||
|
//! - The `Platform` column signifies what Platform this is availible on.
|
||||||
|
//!
|
||||||
|
//! | Backend | Input | Query | Query-Device | Platform |
|
||||||
|
//! |-------------------------------------|--------------------|--------------------|--------------------|---------------------|
|
||||||
|
//! | `Video4Linux`(`input-v4l`) | YES | YES | YES | Linux |
|
||||||
|
//! | `libuvc`(`input-uvc`) | YES | YES | YES | Linux, Windows, Mac |
|
||||||
|
//! | `OpenCV`(`input-opencv`)^ | YES | NO | NO | Linux, Windows, Mac |
|
||||||
|
//! | `IPCamera`(`input-ipcam`/`OpenCV`)^ | YES | NO | NO | Linux, Windows, Mac |
|
||||||
|
//! | `GStreamer`(`input-gst`)^ | YES | YES | YES | Linux, Windows, Mac |
|
||||||
|
//! | `FFMpeg` | * | * | * | Linux, Windows, Mac |
|
||||||
|
//! | `AVFoundation` | * | * | * | Mac |
|
||||||
|
//! | MSMF | * | * | * | Windows |
|
||||||
|
//! | JS/WASM | * | * | * | Web |
|
||||||
|
//!
|
||||||
|
//! *: Planned/WIP
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ^ = No CameraFormat setting support.
|
||||||
|
//!
|
||||||
|
//! ## Feature
|
||||||
|
//! The default feature includes nothing. Anything starting with `input-*` is a feature that enables the specific backend.
|
||||||
|
//! As a general rule of thumb, you would want to keep at least `input-uvc` or other backend that has querying enabled so you can get device information from `nokhwa`.
|
||||||
|
//!
|
||||||
|
//! `input-*` features:
|
||||||
|
//! - `input-v4l`: Enables the `Video4Linux` backend (linux)
|
||||||
|
//! - `input-uvc`: Enables the `libuvc` backend (cross-platform, libuvc statically-linked)
|
||||||
|
//! - `input-opencv`: Enables the `opencv` backend (cross-platform)
|
||||||
|
//! - `input-ipcam`: Enables the use of IP Cameras, please see the `NetworkCamera` struct. Note that this relies on `opencv`, so it will automatically enable the `input-opencv` feature.
|
||||||
|
//! - `input-gst`: Enables the `gstreamer` backend (cross-platform).
|
||||||
|
//!
|
||||||
|
//! Conversely, anything that starts with `output-*` controls a feature that controls the output of something (usually a frame from the camera)
|
||||||
|
//!
|
||||||
|
//! `output-*` features:
|
||||||
|
//! - `output-wgpu`: Enables the API to copy a frame directly into a `wgpu` texture.
|
||||||
|
//!
|
||||||
|
//! You many want to pick and choose to reduce bloat.
|
||||||
|
//! ## Example
|
||||||
|
//! ```.ignore
|
||||||
|
//! // set up the Camera
|
||||||
|
//! let mut camera = Camera::new(
|
||||||
|
//! 0, // index
|
||||||
|
//! Some(CameraFormat::new_from(640, 480, FrameFormat::MJPEG, 30)), // format
|
||||||
|
//! CaptureAPIBackend::AUTO, // what backend to use (let nokhwa decide for itself)
|
||||||
|
//! )
|
||||||
|
//! .unwrap();
|
||||||
|
//! // open stream
|
||||||
|
//! camera.open_stream().unwrap();
|
||||||
|
//! loop {
|
||||||
|
//! let frame = camera.get_frame().unwrap();
|
||||||
|
//! println!("{}, {}", frame.width(), frame.height());
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! They can be found in the `examples` folder.
|
||||||
|
|
||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![allow(clippy::must_use_candidate)]
|
#![allow(clippy::must_use_candidate)]
|
||||||
|
|
||||||
#[allow(clippy::doc_markdown)]
|
|
||||||
/// # nokhwa
|
|
||||||
/// Nokhwa(녹화): Korean word meaning "to record".
|
|
||||||
///
|
|
||||||
/// A Simple-to-use, cross-platform Rust Webcam Capture Library
|
|
||||||
///
|
|
||||||
/// ## Using nokhwa
|
|
||||||
/// You will need the latest stable Rust and Cargo.
|
|
||||||
///
|
|
||||||
/// Nokhwa can be added to your crate by adding it to your `Cargo.toml`:
|
|
||||||
/// ```.ignore
|
|
||||||
/// [dependencies.nokhwa]
|
|
||||||
/// // TODO: replace the "*" with the latest version of `nokhwa`
|
|
||||||
/// version = "*"
|
|
||||||
/// // TODO: add some features
|
|
||||||
/// features = [""]
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Most likely, you will only use functionality provided by the `Camera` struct. If you need lower-level access, you may instead opt to use the raw capture backends found at `nokhwa::backends::capture::*`.
|
|
||||||
/// ## API Support
|
|
||||||
/// The table below lists current Nokhwa API support.
|
|
||||||
/// - The `Backend` column signifies the backend.
|
|
||||||
/// - The `Input` column signifies reading frames from the camera
|
|
||||||
/// - The `Query` column signifies system device list support
|
|
||||||
/// - The `Query-Device` column signifies reading device capabilities
|
|
||||||
/// - The `OS` column signifies what OS this is availible on.
|
|
||||||
///
|
|
||||||
/// | Backend | Input | Query | Query-Device | OS |
|
|
||||||
/// |---------------------------------|--------------------|--------------------|--------------------|---------------------|
|
|
||||||
/// | Video4Linux(`input-v4l`) | :white_check_mark: | :white_check_mark: | :white_check_mark: | Linux |
|
|
||||||
/// | libuvc(`input-uvc`) | :white_check_mark: | :white_check_mark: | :white_check_mark: | Linux, Windows, Mac |
|
|
||||||
/// | OpenCV(`input-opencv`)^ | :white_check_mark: | :x: | :x: | Linux, Windows, Mac |
|
|
||||||
/// | IPCamera(`input-ipcam`/OpenCV)^ | :white_check_mark: | :x: | :x: | Linux, Windows, Mac |
|
|
||||||
/// | GStreamer(`input-gst`)^ | :white_check_mark: | :x: | :white_check_mark: | Linux, Windows, Mac |
|
|
||||||
/// | FFMpeg | * | * | * | Linux, Windows, Mac |
|
|
||||||
/// | AVFoundation | * | * | * | Mac |
|
|
||||||
/// | MSMF | * | * | * | Windows |
|
|
||||||
/// | JS/WASM | * | * | * | Web |
|
|
||||||
///
|
|
||||||
/// :white_check_mark: : Working, :warning: : Experimental, :x: : Not Supported, *: Planned
|
|
||||||
///
|
|
||||||
/// ^ = No CameraFormat setting support.
|
|
||||||
///
|
|
||||||
/// ## Feature
|
|
||||||
/// The default feature includes nothing. Anything starting with `input-*` is a feature that enables the specific backend.
|
|
||||||
/// As a general rule of thumb, you would want to keep at least `input-uvc` or other backend that has querying enabled so you can get device information from `nokhwa`.
|
|
||||||
///
|
|
||||||
/// `input-*` features:
|
|
||||||
/// - `input-v4l`: Enables the `Video4Linux` backend (linux)
|
|
||||||
/// - `input-uvc`: Enables the `libuvc` backend (cross-platform, libuvc statically-linked)
|
|
||||||
/// - `input-opencv`: Enables the `opencv` backend (cross-platform)
|
|
||||||
/// - `input-ipcam`: Enables the use of IP Cameras, please see the `NetworkCamera` struct. Note that this relies on `opencv`, so it will automatically enable the `input-opencv` feature.
|
|
||||||
/// - `input-gst`: Enables the `gstreamer` backend (cross-platform).
|
|
||||||
///
|
|
||||||
/// Conversely, anything that starts with `output-*` controls a feature that controls the output of something (usually a frame from the camera)
|
|
||||||
///
|
|
||||||
/// `output-*` features:
|
|
||||||
/// - `output-wgpu`: Enables the API to copy a frame directly into a `wgpu` texture.
|
|
||||||
///
|
|
||||||
/// You many want to pick and choose to reduce bloat.
|
|
||||||
/// ## Example
|
|
||||||
/// ```rust
|
|
||||||
/// // set up the Camera
|
|
||||||
/// let mut camera = Camera::new(
|
|
||||||
/// 0, // index
|
|
||||||
/// Some(CameraFormat::new_from(640, 480, FrameFormat::MJPEG, 30)), // format
|
|
||||||
/// CaptureAPIBackend::AUTO, // what backend to use (let nokhwa decide for itself)
|
|
||||||
/// )
|
|
||||||
/// .unwrap();
|
|
||||||
/// // open stream
|
|
||||||
/// camera.open_stream().unwrap();
|
|
||||||
/// loop {
|
|
||||||
/// let frame = camera.get_frame().unwrap();
|
|
||||||
/// println!("{}, {}", frame.width(), frame.height());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
/// They can be found in the `examples` folder.
|
|
||||||
|
|
||||||
/// Raw access to each of Nokhwa's backends.
|
/// Raw access to each of Nokhwa's backends.
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
mod camera;
|
mod camera;
|
||||||
@@ -89,8 +89,6 @@ mod error;
|
|||||||
mod network_camera;
|
mod network_camera;
|
||||||
mod query;
|
mod query;
|
||||||
mod utils;
|
mod utils;
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
pub use camera::Camera;
|
pub use camera::Camera;
|
||||||
pub use camera_traits::*;
|
pub use camera_traits::*;
|
||||||
|
|||||||
-144
@@ -1,144 +0,0 @@
|
|||||||
/// Converts $from into $to
|
|
||||||
/// Example usage:
|
|
||||||
/// `tryinto_num(i32, a_unsigned_32_bit_num)`
|
|
||||||
/// Designed to deal with infallible. If not, it should be manually handled.
|
|
||||||
/// # Errors
|
|
||||||
/// If fails to convert(note: should not happen) then you messed up.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! tryinto_num {
|
|
||||||
($to:ty, $from:expr) => {{
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
match <$to>::try_from($from) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(why) => {
|
|
||||||
return Err(crate::NokhwaError::GeneralError(format!(
|
|
||||||
"Failed to convert {}, {}",
|
|
||||||
$from,
|
|
||||||
why.to_string()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes `init-*` functions for you. To be used in `camera.rs`
|
|
||||||
/// Example usage: check `camera.rs`
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! cap_impl_fn {
|
|
||||||
{
|
|
||||||
$( ($backend:ty, $init_fn:ident, $feature:expr, $backend_name:ident) ),+
|
|
||||||
} => {
|
|
||||||
$(
|
|
||||||
paste::paste! {
|
|
||||||
#[cfg(feature = $feature)]
|
|
||||||
fn [< init_ $backend_name>](idx: usize, setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
|
|
||||||
use crate::backends::capture::$backend;
|
|
||||||
match <$backend>::$init_fn(idx, setting) {
|
|
||||||
Ok(cap) => Some(Ok(Box::new(cap))),
|
|
||||||
Err(why) => Some(Err(why)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = $feature))]
|
|
||||||
fn [< init_ $backend_name>](_idx: usize, _setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! cap_impl_matches {
|
|
||||||
{
|
|
||||||
$use_backend: expr, $index:expr, $setting:expr,
|
|
||||||
$( ($feature:expr, $backend:ident, $fn:ident) ),+
|
|
||||||
} => {
|
|
||||||
{
|
|
||||||
let i = $index;
|
|
||||||
let s = $setting;
|
|
||||||
match $use_backend {
|
|
||||||
CaptureAPIBackend::Auto => match figure_out_auto() {
|
|
||||||
Some(cap) => match cap {
|
|
||||||
$(
|
|
||||||
CaptureAPIBackend::$backend => {
|
|
||||||
match cfg!(feature = $feature) {
|
|
||||||
true => {
|
|
||||||
match $fn(i,s) {
|
|
||||||
Some(cap) => match cap {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(why) => return Err(why),
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
_ => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$(
|
|
||||||
CaptureAPIBackend::$backend => {
|
|
||||||
match cfg!(feature = $feature) {
|
|
||||||
true => {
|
|
||||||
match $fn(i,s) {
|
|
||||||
Some(cap) => match cap {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(why) => return Err(why),
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
_ => {
|
|
||||||
return Err(NokhwaError::NotImplemented(
|
|
||||||
"Platform requirements not satisfied.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "input-opencv")]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! vector {
|
|
||||||
( $( $elem:expr ),* ) => {
|
|
||||||
{
|
|
||||||
let mut vector = opencv::core::Vector::new();
|
|
||||||
$(
|
|
||||||
vector.push($elem);
|
|
||||||
)*
|
|
||||||
vector
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
+11
-10
@@ -1,5 +1,4 @@
|
|||||||
use crate::{CameraInfo, CaptureAPIBackend, NokhwaError};
|
use crate::{CameraInfo, CaptureAPIBackend, NokhwaError};
|
||||||
use uvc::Device;
|
|
||||||
|
|
||||||
// TODO: Update as this goes
|
// TODO: Update as this goes
|
||||||
/// Query the system for a list of available devices. Please refer to the API Backends that support `Query`) <br>
|
/// Query the system for a list of available devices. Please refer to the API Backends that support `Query`) <br>
|
||||||
@@ -72,8 +71,9 @@ pub fn query_devices(api: CaptureAPIBackend) -> Result<Vec<CameraInfo>, NokhwaEr
|
|||||||
// TODO: More
|
// TODO: More
|
||||||
|
|
||||||
#[cfg(feature = "input-v4l")]
|
#[cfg(feature = "input-v4l")]
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
|
fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
|
||||||
return Ok({
|
Ok({
|
||||||
let camera_info: Vec<CameraInfo> = v4l::context::enum_devices()
|
let camera_info: Vec<CameraInfo> = v4l::context::enum_devices()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
@@ -87,7 +87,7 @@ fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
camera_info
|
camera_info
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "input-v4l"))]
|
#[cfg(not(feature = "input-v4l"))]
|
||||||
@@ -99,6 +99,7 @@ fn query_v4l() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
|
|
||||||
#[cfg(feature = "input-uvc")]
|
#[cfg(feature = "input-uvc")]
|
||||||
fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
||||||
|
use uvc::Device;
|
||||||
let context = match uvc::Context::new() {
|
let context = match uvc::Context::new() {
|
||||||
Ok(ctx) => ctx,
|
Ok(ctx) => ctx,
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
@@ -112,7 +113,7 @@ fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
let usb_devices = usb_enumeration::enumerate(None, None);
|
let usb_devices = usb_enumeration::enumerate(None, None);
|
||||||
let uvc_devices = match context.devices() {
|
let uvc_devices = match context.devices() {
|
||||||
Ok(devs) => {
|
Ok(devs) => {
|
||||||
let device_vec: Vec<Device> = devs.map(|d| d).collect();
|
let device_vec: Vec<Device> = devs.collect();
|
||||||
device_vec
|
device_vec
|
||||||
}
|
}
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
@@ -126,9 +127,9 @@ fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
let mut camera_info_vec = vec![];
|
let mut camera_info_vec = vec![];
|
||||||
let mut counter = 0_usize;
|
let mut counter = 0_usize;
|
||||||
|
|
||||||
// Optimize this O(n*m) algorithem
|
// Optimize this O(n*m) algorithm
|
||||||
for usb_dev in usb_devices.iter() {
|
for usb_dev in &usb_devices {
|
||||||
for uvc_dev in uvc_devices.iter() {
|
for uvc_dev in &uvc_devices {
|
||||||
if let Ok(desc) = uvc_dev.description() {
|
if let Ok(desc) = uvc_dev.description() {
|
||||||
if desc.product_id == usb_dev.product_id && desc.vendor_id == usb_dev.vendor_id {
|
if desc.product_id == usb_dev.product_id && desc.vendor_id == usb_dev.vendor_id {
|
||||||
let name = usb_dev
|
let name = usb_dev
|
||||||
@@ -138,8 +139,8 @@ fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
"{}:{} {} {}",
|
"{}:{} {} {}",
|
||||||
desc.vendor_id,
|
desc.vendor_id,
|
||||||
desc.product_id,
|
desc.product_id,
|
||||||
desc.manufacturer.unwrap_or("Generic".to_string()),
|
desc.manufacturer.unwrap_or_else(|| "Generic".to_string()),
|
||||||
desc.product.unwrap_or("Camera".to_string())
|
desc.product.unwrap_or_else(|| "Camera".to_string())
|
||||||
))
|
))
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
@@ -154,7 +155,7 @@ fn query_uvc() -> Result<Vec<CameraInfo>, NokhwaError> {
|
|||||||
"{}:{} {}",
|
"{}:{} {}",
|
||||||
desc.vendor_id,
|
desc.vendor_id,
|
||||||
desc.product_id,
|
desc.product_id,
|
||||||
desc.serial_number.unwrap_or("".to_string())
|
desc.serial_number.unwrap_or_else(|| "".to_string())
|
||||||
),
|
),
|
||||||
counter,
|
counter,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -450,3 +450,29 @@ pub fn yuyv444_to_rgb888(y: i32, u: i32, v: i32) -> [u8; 3] {
|
|||||||
let b = ((c298 + 516 * d + 128) >> 8).clamp(0, 255) as u8;
|
let b = ((c298 + 516 * d + 128) >> 8).clamp(0, 255) as u8;
|
||||||
[r, g, b]
|
[r, g, b]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `OpenCV` backend supports both native cameras and IP Cameras, so this is an enum to differentiate them
|
||||||
|
/// The `IPCamera`'s string follows the pattern
|
||||||
|
/// ```.ignore
|
||||||
|
/// <protocol>://<IP>:<port>/
|
||||||
|
/// ```
|
||||||
|
/// but please consult the manufacturer's specification for more details.
|
||||||
|
/// The index is a standard webcam index.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum CameraIndexType {
|
||||||
|
Index(u32),
|
||||||
|
IPCamera(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CameraIndexType {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CameraIndexType::Index(idx) => {
|
||||||
|
write!(f, "{}", idx)
|
||||||
|
}
|
||||||
|
CameraIndexType::IPCamera(ip) => {
|
||||||
|
write!(f, "{}", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user