0.10 preliminary work

This commit is contained in:
l1npengtul
2022-05-07 03:23:54 +09:00
parent fc1317bd36
commit 78eb1fb7bb
12 changed files with 187 additions and 170 deletions
+15 -22
View File
@@ -10,6 +10,10 @@ repository = "https://github.com/l1npengtul/nokhwa"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows"]
exclude = ["examples/threaded-capture", "examples/capture"]
[lib]
crate-type = ["cdylib", "rlib"]
@@ -28,7 +32,7 @@ input-jscam = ["web-sys", "js-sys", "wasm-bindgen-futures", "wasm-bindgen", "was
output-wgpu = ["wgpu"]
output-wasm = ["input-jscam"]
output-threaded = ["parking_lot"]
small-wasm = ["wee_alloc"]
small-wasm = []
docs-only = ["input-v4l", "input-opencv", "input-ipcam", "input-gst", "input-msmf", "input-avfoundation", "input-jscam","output-wgpu", "output-wasm", "output-threaded"]
docs-nolink = ["glib/dox", "gstreamer-app/dox", "gstreamer/dox", "gstreamer-video/dox", "opencv/docs-only"]
docs-features = []
@@ -37,35 +41,29 @@ test-fail-warning = []
[dependencies]
thiserror = "1.0"
paste = "1.0"
anymap = "1.0.0-beta.2"
enum_dispatch = "0.3"
[dependencies.flume]
version = "0.10"
optional = true
[dependencies.image]
version = "0.23"
version = "0.24"
default-features = false
[target.'cfg(not(target_arch = "wasm"))'.dependencies.mozjpeg]
[dependencies.mozjpeg]
version = "0.9"
optional = true
[target.'cfg(target_os = "linux")'.dependencies.v4l]
[dependencies.v4l]
version = "0.12"
optional = true
[target.'cfg(target_os = "linux")'.dependencies.v4l2-sys-mit]
[dependencies.v4l2-sys-mit]
version = "0.2"
optional = true
[dependencies.ouroboros]
version = "0.14"
optional = true
# [dependencies.uvc]
# version = "0.2"
# optional = true
[dependencies.usb_enumeration]
version = "0.1.2"
optional = true
@@ -75,17 +73,17 @@ version = "^0.12"
optional = true
[dependencies.opencv]
version = "0.62"
version = "0.63"
features = ["clang-runtime"]
optional = true
[dependencies.nokhwa-bindings-windows]
version = "0.3"
version = "0.3.4"
path = "nokhwa-bindings-windows"
optional = true
[dependencies.nokhwa-bindings-macos]
version = "0.1"
version = "0.1.1"
path = "nokhwa-bindings-macos"
optional = true
@@ -111,7 +109,6 @@ optional = true
[dependencies.web-sys]
version = "0.3"
# why
features = [
"console",
"CanvasRenderingContext2d",
@@ -146,12 +143,8 @@ optional = true
version = "0.9"
optional = true
[dependencies.wee_alloc]
version = "0.4"
optional = true
[dependencies.parking_lot]
version = "0.11"
version = "0.12"
optional = true
[dependencies.lazy_static]
Vendored
-135
View File
@@ -1,135 +0,0 @@
pipeline {
agent {
node {
label 'ci_linux'
}
}
stages {
stage('Sanity Check') {
steps {
echo '$BUILD_TAG'
scmSkip(deleteBuild: true, skipPattern: '.*\\[ci skip\\].*')
}
}
stage('Cargo RustFMT') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(deleteBuild: true, skipPattern: '.*\\[ci skip\\].*')
sh 'rustup update stable'
sh 'cargo fmt --all -- --check'
}
}
stage('Build, Clippy') {
parallel {
stage('V4L2') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update stable'
sh 'cargo clippy --features "input-v4l, output-wgpu, test-fail-warning"'
}
}
stage('Media Foundation') {
agent {
node {
label 'ci_windows'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
pwsh(script: 'rustup update stable', returnStatus: true)
pwsh(script: 'cargo clippy --features "input-msmf, output-wgpu, test-fail-warning"', returnStatus: true, returnStdout: true)
}
}
stage('AVFoundation') {
steps {
sh 'echo TODO'
}
}
stage('libUVC') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update stable'
sh 'cargo clippy --features "input-uvc, output-wgpu, test-fail-warning"'
}
}
stage('OpenCV IPCamera') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update stable'
sh 'cargo clippy --features "input-opencv, input-ipcam, output-wgpu, test-fail-warning"'
}
}
stage('GStreamer') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update nightly'
sh 'cargo clippy --features "input-gst, output-wgpu, test-fail-warning"'
}
}
stage('JSCamera/WASM') {
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update stable'
sh 'wasm-pack build --release -- --features "input-jscam, output-wasm, test-fail-warning" --no-default-features'
sh 'cargo clippy --features "input-jscam, output-wasm, test-fail-warning" --no-default-features'
}
}
}
}
stage('RustDOC') {
agent {
node {
label 'ci_linux'
}
}
steps {
scmSkip(skipPattern: '.*\\[ci skip\\].*', deleteBuild: true)
sh 'rustup update nightly'
sh 'cargo +nightly doc --features "docs-only, docs-nolink, docs-features, test-fail-warning" --no-deps --release'
}
}
}
}
+1
View File
@@ -1303,6 +1303,7 @@ pub mod avfoundation {
pub enum AVFourCC {
YUV2,
MJPEG,
GRAY8,
}
// Localized Name
+1 -1
View File
@@ -155,7 +155,7 @@ impl GStreamerCaptureDevice {
}
}
impl CaptureBackendTrait for GStreamerCaptureDevice {
impl GStreamerCaptureDevice {
fn backend(&self) -> CaptureAPIBackend {
CaptureAPIBackend::GStreamer
}
+17
View File
@@ -35,6 +35,7 @@ use v4l::{
use std::any::Any;
pub use v4l::control::{Control, Description, Flags};
use v4l::video::Output;
/// Generates a camera control from a device and a description of control
/// # Error
@@ -212,6 +213,7 @@ impl<'a> V4LCaptureDevice<'a> {
let fourcc = match camera_format.format() {
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
FrameFormat::YUYV => FourCC::new(b"YUYV"),
FrameFormat::GRAY8 => FourCC::new(b"GRAY"),
};
let new_param = Parameters::with_fps(camera_format.frame_rate());
@@ -284,6 +286,7 @@ impl<'a> V4LCaptureDevice<'a> {
let format = match fourcc {
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
FrameFormat::YUYV => FourCC::new(b"YUYV"),
FrameFormat::GRAY8 => FourCC::new("GRAY"),
};
match v4l::video::Capture::enum_framesizes(&self.device, format) {
@@ -311,6 +314,7 @@ impl<'a> V4LCaptureDevice<'a> {
}
/// Get the inner device (immutable) for e.g. Controls
/// apps bloodtests contact css images index index.html injectionsupplies transfem transmasc
#[allow(clippy::must_use_candidate)]
pub fn inner_device(&self) -> &Device {
&self.device
@@ -320,6 +324,18 @@ impl<'a> V4LCaptureDevice<'a> {
pub fn inner_device_mut(&mut self) -> &mut Device {
&mut self.device
}
/// Force refreshes the inner [`CameraFormat`] state.
pub fn force_refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
match (self.device.params(), self.device.format()) {
(Ok(params), Ok(format)) => {
let new_format = CameraFormat::new(params.capabilities.)
}
(_, _) => {
return Err(NokhwaError::GetPropertyError { property: "parameters".to_string(), error: why.to_string() })
}
}
}
}
impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
@@ -409,6 +425,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
let format = match fourcc {
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
FrameFormat::YUYV => FourCC::new(b"YUYV"),
FrameFormat::GRAY8 => {}
};
let mut res_map = HashMap::new();
for res in resolutions {
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::pixel_format::{PixelFormat, PixelFormats};
use crate::{FrameFormat, NokhwaError, Resolution};
use image::ImageBuffer;
#[derive(Clone, Debug, Default, Hash, PartialOrd, PartialEq)]
#[cfg_attr("serde", Serialize, Deserialize)]
pub struct Buffer {
resolution: Resolution,
buffer: Vec<u8>,
}
impl Buffer {
pub fn new(res: Resolution, buf: Vec<u8>) -> Self {
Self {
resolution: res,
buffer: buf,
}
}
pub fn to_image_with_custom_format<I>(self) -> Result<ImageBuffer<I::Output, Vec<u8>>, NokhwaError>
where
I: PixelFormat,
{
ImageBuffer::from_raw(
self.resolution.width_x,
self.resolution.height_y,
self.buffer,
).ok_or(NokhwaError::ProcessFrameError {
src: ,
destination: "".to_string(),
error: "".to_string()
})
}
}
+19 -10
View File
@@ -29,14 +29,19 @@ use wgpu::{
};
/// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use.
pub struct Camera {
pub struct Camera<C>
where
C: CaptureBackendTrait,
{
idx: usize,
backend: Box<dyn CaptureBackendTrait>,
backend: C,
backend_api: CaptureAPIBackend,
}
#[allow(clippy::nonminimal_bool)]
impl Camera {
impl Camera<C>
where
C: CaptureBackendTrait,
{
/// Create a new camera from an `index` and `format`
/// # Errors
/// This will error if you either have a bad platform configuration (e.g. `input-v4l` but not on linux) or the backend cannot create the camera (e.g. permission denied).
@@ -386,6 +391,7 @@ impl Camera {
FrameFormat::YUYV => {
crate::utils::buf_yuyv422_to_rgb(&raw_frame, buffer, convert_rgba)?
}
FrameFormat::GRAY8 => {}
};
Ok(())
@@ -459,7 +465,10 @@ impl Camera {
}
}
impl Drop for Camera {
impl Drop for Camera<C>
where
C: CaptureBackendTrait,
{
fn drop(&mut self) {
self.stop_stream().unwrap();
}
@@ -496,7 +505,7 @@ macro_rules! cap_impl_fn {
$(
paste::paste! {
#[cfg ($cfg) ]
fn [< init_ $backend_name>](idx: usize, setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
fn [< init_ $backend_name>]<C: CaptureBackendTrait>(idx: usize, setting: Option<CameraFormat>) -> Option<Result<C, NokhwaError>> {
use crate::backends::capture::$backend;
match <$backend>::$init_fn(idx, setting) {
Ok(cap) => Some(Ok(Box::new(cap))),
@@ -504,7 +513,7 @@ macro_rules! cap_impl_fn {
}
}
#[cfg(not( $cfg ))]
fn [< init_ $backend_name>](_idx: usize, _setting: Option<CameraFormat>) -> Option<Result<Box<dyn CaptureBackendTrait>, NokhwaError>> {
fn [< init_ $backend_name>]<C: CaptureBackendTrait>(_idx: usize, _setting: Option<CameraFormat>) -> Option<Result<C, NokhwaError>> {
None
}
}
@@ -603,11 +612,11 @@ cap_impl_fn! {
(AVFoundationCaptureDevice, new, all(feature = "input-avfoundation", any(target_os = "macos", target_os = "ios")), avfoundation)
}
fn init_camera(
fn init_camera<C: CaptureBackendTrait + Send + Sync>(
index: usize,
format: Option<CameraFormat>,
backend: CaptureAPIBackend,
) -> Result<Box<dyn CaptureBackendTrait>, NokhwaError> {
) -> Result<C, NokhwaError> {
let camera_backend = cap_impl_matches! {
backend, index, format,
("input-v4l", Video4Linux, init_v4l),
@@ -621,4 +630,4 @@ fn init_camera(
}
#[cfg(feature = "output-threaded")]
unsafe impl Send for Camera {}
unsafe impl Send for Camera<C> where C: CaptureBackendTrait {}
+6 -2
View File
@@ -21,6 +21,7 @@ use crate::{
};
use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbaImage};
use crate::pixel_format::PixelFormat;
use std::{any::Any, borrow::Cow, collections::HashMap};
#[cfg(feature = "output-wgpu")]
use wgpu::{
@@ -36,7 +37,10 @@ use wgpu::{
/// - Backends, if not provided with a camera format, will be spawned with 640x480@15 FPS, MJPEG [`CameraFormat`].
/// - Behaviour can differ from backend to backend. While the [`Camera`](crate::camera::Camera) struct abstracts most of this away, if you plan to use the raw backend structs please read the `Quirks` section of each backend.
/// - If you call [`stop_stream()`](CaptureBackendTrait::stop_stream()), you will usually need to call [`open_stream()`](CaptureBackendTrait::open_stream()) to get more frames from the camera.
pub trait CaptureBackendTrait: Send {
pub trait CaptureBackendTrait {
/// Initializes the camera. You must call this before any other function.
fn init(&mut self) -> Result<CameraFormat, NokhwaError>;
/// Returns the current backend used.
fn backend(&self) -> CaptureAPIBackend;
@@ -209,7 +213,7 @@ pub trait CaptureBackendTrait: Send {
queue: &WgpuQueue,
label: Option<&'a str>,
) -> Result<WgpuTexture, NokhwaError> {
use std::num::NonZeroU32;
use std::{convert::TryFrom, num::NonZeroU32};
let frame = self.frame()?;
let rgba_frame: RgbaImage = frame.convert();
+2
View File
@@ -21,6 +21,8 @@ use thiserror::Error;
#[allow(clippy::module_name_repetitions)]
#[derive(Error, Debug, Clone)]
pub enum NokhwaError {
#[error("Unitialized Camera. Call `init()` first!")]
UnitializedError,
#[error("Could not initialize {backend}: {error}")]
InitializeError {
backend: CaptureAPIBackend,
+2
View File
@@ -28,6 +28,7 @@
/// Raw access to each of Nokhwa's backends.
pub mod backends;
pub mod buffer;
mod camera;
mod camera_traits;
mod error;
@@ -40,6 +41,7 @@ pub mod js_camera;
#[cfg(feature = "input-ipcam")]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-ipcam")))]
pub mod network_camera;
pub mod pixel_format;
mod query;
/// A camera that runs in a different thread and can call your code based on callbacks.
#[cfg(feature = "output-threaded")]
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::buffer_output::{BufferOutput, GrayU8, RgbU8};
use image::{Luma, Pixel, Rgb};
use std::fmt::Debug;
use std::hash::Hash;
pub trait PixelFormat: Copy + Clone + Debug + Default + Hash + Send + Sync {
type Output: Pixel;
const CODE: &'static str;
fn code(&self) -> &'static str {
CODE
}
}
#[derive(Copy, Clone, Debug, Default, Hash)]
pub struct Mjpeg;
impl PixelFormat for Mjpeg {
type Output = Rgb<u8>;
const CODE: &'static str = "MJPG";
}
#[derive(Copy, Clone, Debug, Default, Hash)]
pub struct Yuyv;
impl PixelFormat for Yuyv {
type Output = Rgb<u8>;
const CODE: &'static str = "YUYV";
}
#[derive(Copy, Clone, Debug, Default, Hash)]
pub struct Gray;
impl PixelFormat for Gray {
type Output = Luma<u8>;
const CODE: &'static str = "GRAY";
}
+16
View File
@@ -49,6 +49,7 @@ use nokhwa_bindings_windows::{
use uvc::StreamFormat;
#[cfg(all(feature = "input-v4l", target_os = "linux"))]
use v4l::{control::Description, Format, FourCC};
use crate::pixel_format::PixelFormat;
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
/// - YUYV is a mathematical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr)
@@ -59,6 +60,7 @@ use v4l::{control::Description, Format, FourCC};
pub enum FrameFormat {
MJPEG,
YUYV,
GRAY8,
}
impl Display for FrameFormat {
@@ -70,10 +72,19 @@ impl Display for FrameFormat {
FrameFormat::YUYV => {
write!(f, "YUYV")
}
FrameFormat::GRAY8 => {
write!(f, "GRAY8")
}
}
}
}
impl<P> From<P> for FrameFormat where P: PixelFormat {
fn from(px: P) -> Self {
match P::
}
}
#[cfg(feature = "input-uvc")]
impl From<FrameFormat> for uvc::FrameFormat {
fn from(ff: FrameFormat) -> Self {
@@ -93,6 +104,7 @@ impl From<MFFrameFormat> for FrameFormat {
match mf_ff {
MFFrameFormat::MJPEG => FrameFormat::MJPEG,
MFFrameFormat::YUYV => FrameFormat::YUYV,
MFFrameFormat::GRAY8 => FrameFormat::GRAY8,
}
}
}
@@ -106,6 +118,7 @@ impl From<FrameFormat> for MFFrameFormat {
match ff {
FrameFormat::MJPEG => MFFrameFormat::MJPEG,
FrameFormat::YUYV => MFFrameFormat::YUYV,
FrameFormat::GRAY8 => MFFrameFormat::GRAY8, //FIXME
}
}
}
@@ -126,6 +139,7 @@ impl From<AVFourCC> for FrameFormat {
match av_fcc {
AVFourCC::YUV2 => FrameFormat::YUYV,
AVFourCC::MJPEG => FrameFormat::MJPEG,
AVFourCC::GRAY8 => FrameFormat::GRAY8,
}
}
}
@@ -146,6 +160,7 @@ impl From<FrameFormat> for AVFourCC {
match ff {
FrameFormat::MJPEG => AVFourCC::MJPEG,
FrameFormat::YUYV => AVFourCC::YUV2,
FrameFormat::GRAY8 => AVFourCC::GRAY8,
}
}
}
@@ -419,6 +434,7 @@ impl From<CameraFormat> for Format {
let pxfmt = match cam_fmt.format() {
FrameFormat::MJPEG => FourCC::new(b"MJPG"),
FrameFormat::YUYV => FourCC::new(b"YUYV"),
FrameFormat::GRAY8 => FourCC::new(b"GREY"),
};
Format::new(cam_fmt.width(), cam_fmt.height(), pxfmt)