buffer impl, fixes for some tings, etc

This commit is contained in:
l1npengtul
2022-05-12 02:29:30 +09:00
parent 78eb1fb7bb
commit d148dc208a
7 changed files with 153 additions and 76 deletions
+10 -2
View File
@@ -19,13 +19,14 @@ crate-type = ["cdylib", "rlib"]
[features]
default = ["flume", "decoding"]
serialize = ["serde"]
decoding = ["mozjpeg"]
input-v4l = ["v4l", "v4l2-sys-mit"]
input-msmf = ["nokhwa-bindings-windows"]
input-avfoundation = ["nokhwa-bindings-macos"]
# Re-enable it once soundness has been proven + mozjpeg is updated to 0.9.x
# input-uvc = ["uvc", "uvc/vendor", "ouroboros", "usb_enumeration", "lazy_static"]
input-opencv = ["opencv", "opencv/clang-runtime"]
input-opencv = ["opencv", "opencv/rgb", "rgb"]
input-ipcam = ["input-opencv"]
input-gst = ["gstreamer", "glib", "gstreamer-app", "gstreamer-video", "regex", "parking_lot"]
input-jscam = ["web-sys", "js-sys", "wasm-bindgen-futures", "wasm-bindgen", "wasm-rs-async-executor"]
@@ -44,6 +45,10 @@ paste = "1.0"
anymap = "1.0.0-beta.2"
enum_dispatch = "0.3"
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.flume]
version = "0.10"
optional = true
@@ -74,7 +79,10 @@ optional = true
[dependencies.opencv]
version = "0.63"
features = ["clang-runtime"]
optional = true
[dependencies.rgb]
version = "0.8"
optional = true
[dependencies.nokhwa-bindings-windows]
+14
View File
@@ -374,6 +374,11 @@ impl GStreamerCaptureDevice {
.insert(Resolution::new(width as u32, height as u32), fps_vec);
}
}
unsupported => {
return Err(NokhwaError::NotImplementedError(format!(
"Not supported frame format {unsupported:?}"
)))
}
}
}
}
@@ -581,6 +586,9 @@ fn webcam_pipeline(device: &str, camera_format: CameraFormat) -> String {
FrameFormat::YUYV => {
format!("autovideosrc location=/dev/video{} ! video/x-raw,format=YUY2,width={},height={},framerate={}/1 ! appsink name=appsink async=false sync=false", device, camera_format.width(), camera_format.height(), camera_format.frame_rate())
}
_ => {
format!("unsupproted! if you see this, switch to something else!")
}
}
}
@@ -593,6 +601,9 @@ fn webcam_pipeline(device: &str, camera_format: CameraFormat) -> String {
FrameFormat::YUYV => {
format!("v4l2src device=/dev/video{} ! video/x-raw,format=YUY2,width={},height={},framerate={}/1 ! appsink name=appsink async=false sync=false", device, camera_format.width(), camera_format.height(), camera_format.frame_rate())
}
_ => {
format!("unsupproted! if you see this, switch to something else!")
}
}
}
@@ -605,6 +616,9 @@ fn webcam_pipeline(device: &str, camera_format: CameraFormat) -> String {
FrameFormat::YUYV => {
format!("ksvideosrc device_index={} ! video/x-raw,format=YUY2,width={},height={},framerate={}/1 ! appsink name=appsink async=false sync=false", device, camera_format.width(), camera_format.height(), camera_format.frame_rate())
}
_ => {
format!("unsupproted! if you see this, switch to something else!")
}
}
}
+12 -3
View File
@@ -286,7 +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"),
FrameFormat::GRAY8 => FourCC::new(b"GRAY"),
};
match v4l::video::Capture::enum_framesizes(&self.device, format) {
@@ -329,10 +329,19 @@ impl<'a> V4LCaptureDevice<'a> {
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.)
// FIXME: actually handle the fractions??????
self.camera_format = CameraFormat::new(
Resolution::new(format.width, format.height),
FrameFormat::from(format.fourcc),
params.interval.numerator,
);
return Ok(());
}
(_, _) => {
return Err(NokhwaError::GetPropertyError { property: "parameters".to_string(), error: why.to_string() })
return Err(NokhwaError::GetPropertyError {
property: "parameters".to_string(),
error: why.to_string(),
})
}
}
}
+58 -10
View File
@@ -17,35 +17,83 @@
use crate::pixel_format::{PixelFormat, PixelFormats};
use crate::{FrameFormat, NokhwaError, Resolution};
use image::ImageBuffer;
#[cfg(feature = "input-opencv")]
use opencv::core::Mat;
#[cfg(feature = "input-opencv")]
use rgb::{FromSlice, RGB};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Hash, PartialOrd, PartialEq)]
#[cfg_attr("serde", Serialize, Deserialize)]
#[cfg_attr(feature = "serde", Serialize, Deserialize)]
pub struct Buffer {
resolution: Resolution,
buffer: Vec<u8>,
source_frame_format: FrameFormat,
}
impl Buffer {
pub fn new(res: Resolution, buf: Vec<u8>) -> Self {
pub fn new(res: Resolution, buf: Vec<u8>, source_frame_format: FrameFormat) -> Self {
Self {
resolution: res,
buffer: buf,
source_frame_format,
}
}
pub fn to_image_with_custom_format<I>(self) -> Result<ImageBuffer<I::Output, Vec<u8>>, NokhwaError>
pub fn to_image_with_custom_format<F>(
self,
) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError>
where
I: PixelFormat,
F: PixelFormat,
{
if self.source_frame_format != F::CODE {
return Err(NokhwaError::ProcessFrameError {
src: self.source_frame_format,
destination: F::CODE.to_string(),
error: "Assertion failed, wrong source!".to_string(),
});
}
ImageBuffer::from_raw(
self.resolution.width_x,
self.resolution.height_y,
self.buffer,
).ok_or(NokhwaError::ProcessFrameError {
src: ,
destination: "".to_string(),
error: "".to_string()
)
.ok_or(NokhwaError::ProcessFrameError {
src: F::CODE,
destination: stringify!(I::Output).to_string(),
error: "Buffer too small".to_string(),
})
}
#[cfg(feature = "input-opencv")]
pub fn to_opencv_mat(self) -> Result<Mat, NokhwaError> {
Ok(match self.source_frame_format {
FrameFormat::MJPEG | FrameFormat::YUYV => Mat::from_slice_2d(
self.buffer
.as_rgb()
.chunks(self.resolution.height_y as usize)
.collect::<&[&[RGB<u8>]]>(),
),
FrameFormat::GRAY8 => Mat::from_slice_2d(
self.buffer
.chunks(self.resolution.height_y as usize)
.collect::<&[&[u8]]>(),
),
}
.map_err(|why| NokhwaError::ProcessFrameError {
src: self.source_frame_format,
destination: "OpenCV Mat".to_string(),
error: why.to_string(),
})?)
}
pub fn resolution(&self) -> Resolution {
self.resolution
}
pub fn buffer(&self) -> &[u8] {
&self.buffer
}
pub fn source_frame_format(&self) -> FrameFormat {
self.source_frame_format
}
}
+23 -10
View File
@@ -14,14 +14,15 @@
* limitations under the License.
*/
use crate::buffer::Buffer;
use crate::pixel_format::PixelFormat;
use crate::{
error::NokhwaError,
utils::{CameraFormat, CameraInfo, FrameFormat, Resolution},
CameraControl, CaptureAPIBackend, KnownCameraControls,
};
use image::{buffer::ConvertBuffer, ImageBuffer, Rgb, RgbaImage};
use crate::pixel_format::PixelFormat;
use opencv::imgproc::FloodFillFlags;
use std::{any::Any, borrow::Cow, collections::HashMap};
#[cfg(feature = "output-wgpu")]
use wgpu::{
@@ -48,7 +49,7 @@ pub trait CaptureBackendTrait {
fn camera_info(&self) -> &CameraInfo;
/// Gets the current [`CameraFormat`].
fn camera_format(&self) -> CameraFormat;
fn camera_format(&self) -> Result<CameraFormat, NokhwaError>;
/// Will set the current [`CameraFormat`]
/// This will reset the current stream if used while stream is opened.
@@ -70,7 +71,7 @@ pub trait CaptureBackendTrait {
fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError>;
/// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]).
fn resolution(&self) -> Resolution;
fn resolution(&self) -> Result<Resolution, NokhwaError>;
/// Will set the current [`Resolution`]
/// This will reset the current stream if used while stream is opened.
@@ -79,7 +80,7 @@ pub trait CaptureBackendTrait {
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError>;
/// Gets the current camera framerate (See: [`CameraFormat`]).
fn frame_rate(&self) -> u32;
fn frame_rate(&self) -> Result<u32, NokhwaError>;
/// Will set the current framerate
/// This will reset the current stream if used while stream is opened.
@@ -88,7 +89,7 @@ pub trait CaptureBackendTrait {
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError>;
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
fn frame_format(&self) -> FrameFormat;
fn frame_format(&self) -> Result<FrameFormat, NokhwaError>;
/// Will set the current [`FrameFormat`]
/// This will reset the current stream if used while stream is opened.
@@ -155,7 +156,16 @@ pub trait CaptureBackendTrait {
/// # Errors
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), the decoding fails (e.g. MJPEG -> u8), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet,
/// this will error.
fn frame(&mut self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError>;
fn frame(&mut self) -> Result<Buffer, NokhwaError>;
/// Will get a frame from the camera as a Raw RGB image buffer. Depending on the backend, if you have not called [`open_stream()`](CaptureBackendTrait::open_stream()) before you called this,
/// it will either return an error.
/// # Errors
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), the decoding fails (e.g. MJPEG -> u8), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet,
/// or if the PixelFormat is invalid, this will error.
fn frame_typed<F: PixelFormat>(
&mut self,
) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError>;
/// Will get a frame from the camera **without** any processing applied, meaning you will usually get a frame you need to decode yourself.
/// # Errors
@@ -174,11 +184,14 @@ pub trait CaptureBackendTrait {
/// Directly writes the current frame(RGB24) into said `buffer`. If `convert_rgba` is true, the buffer written will be written as an RGBA frame instead of a RGB frame. Returns the amount of bytes written on successful capture.
/// # Errors
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error.
fn write_frame_to_buffer(
fn write_frame_to_buffer<F>(
&mut self,
buffer: &mut [u8],
convert_rgba: bool,
) -> Result<usize, NokhwaError> {
write_alpha: bool,
) -> Result<usize, NokhwaError>
where
F: PixelFormat,
{
let resolution = self.resolution();
let frame = self.frame_raw()?;
if convert_rgba {
+2 -32
View File
@@ -15,6 +15,7 @@
*/
use crate::buffer_output::{BufferOutput, GrayU8, RgbU8};
use crate::FrameFormat;
use image::{Luma, Pixel, Rgb};
use std::fmt::Debug;
use std::hash::Hash;
@@ -22,36 +23,5 @@ 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";
const SUPPORTED_CODES: &'static [FrameFormat];
}
+34 -19
View File
@@ -14,15 +14,8 @@
* limitations under the License.
*/
use crate::pixel_format::PixelFormat;
use crate::NokhwaError;
use std::{
borrow::{Borrow, Cow},
cmp::Ordering,
fmt::{Display, Formatter},
};
#[cfg(feature = "output-wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(any(
all(
feature = "input-avfoundation",
@@ -45,11 +38,20 @@ use nokhwa_bindings_windows::{
MFCameraFormat, MFControl, MFFrameFormat, MFResolution, MediaFoundationControls,
MediaFoundationDeviceDescriptor,
};
use serde::{Deserialize, Serialize};
#[cfg(feature = serde)]
use serde::{Deserialize, Serialize};
use std::{
borrow::{Borrow, Cow},
cmp::Ordering,
fmt::{Display, Formatter},
};
#[cfg(feature = "input-uvc")]
use uvc::StreamFormat;
#[cfg(all(feature = "input-v4l", target_os = "linux"))]
use v4l::{control::Description, Format, FourCC};
use crate::pixel_format::PixelFormat;
#[cfg(feature = "output-wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
/// 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)
@@ -57,6 +59,7 @@ use crate::pixel_format::PixelFormat;
/// # JS-WASM
/// This is exported as `FrameFormat`
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FrameFormat {
MJPEG,
YUYV,
@@ -64,7 +67,7 @@ pub enum FrameFormat {
}
impl Display for FrameFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FrameFormat::MJPEG => {
write!(f, "MJPEG")
@@ -79,9 +82,12 @@ impl Display for FrameFormat {
}
}
impl<P> From<P> for FrameFormat where P: PixelFormat {
fn from(px: P) -> Self {
match P::
impl<P> From<P> for FrameFormat
where
P: PixelFormat,
{
fn from(_: P) -> Self {
P::CODE
}
}
@@ -171,6 +177,7 @@ impl From<FrameFormat> for AVFourCC {
/// # JS-WASM
/// This is exported as `JSResolution`
#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = JSResolution))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct Resolution {
pub width_x: u32,
@@ -225,7 +232,7 @@ impl Resolution {
}
impl Display for Resolution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}x{}", self.x(), self.y())
}
}
@@ -296,6 +303,7 @@ impl From<AVVideoResolution> for Resolution {
/// This is a convenience struct that holds all information about the format of a webcam stream.
/// It consists of a [`Resolution`], [`FrameFormat`], and a frame rate(u8).
#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CameraFormat {
resolution: Resolution,
format: FrameFormat,
@@ -474,6 +482,7 @@ impl From<CameraFormat> for CaptureDeviceFormatDescriptor {
/// This is exported as a `JSCameraInfo`.
#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = JSCameraInfo))]
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CameraInfo {
human_name: String,
description: String,
@@ -488,10 +497,13 @@ impl CameraInfo {
/// This is exported as a constructor for [`CameraInfo`].
#[must_use]
#[cfg_attr(feature = "output-wasm", wasm_bindgen(constructor))]
// OK, i just checkeed back on this code. WTF was I on when I wrote `&(impl AsRef<str> + ?Sized)` ????
// I need to get on the same shit that my previous self was on, because holy shit that stuff is strong as FUCK!
// Finally fixed this insanity. Hopefully I didnt torment anyone by actually putting this in a stable release.
pub fn new(
human_name: &(impl AsRef<str> + ?Sized),
description: &(impl AsRef<str> + ?Sized),
misc: &(impl AsRef<str> + ?Sized),
human_name: impl AsRef<str>,
description: impl AsRef<str>,
misc: impl AsRef<str>,
index: CameraIndex,
) -> Self {
CameraInfo {
@@ -648,6 +660,7 @@ impl From<AVCaptureDeviceDescriptor> for CameraInfo {
/// These can control the picture brightness, etc. <br>
/// Note that not all backends/devices support all these. Run [`supported_camera_controls()`](crate::CaptureBackendTrait::supported_camera_controls) to see which ones can be set.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum KnownCameraControls {
Brightness,
Contrast,
@@ -737,7 +750,7 @@ impl From<MFControl> for KnownCameraControls {
}
#[cfg(all(feature = "input-v4l", target_os = "linux"))]
impl std::convert::TryFrom<Description> for KnownCameraControls {
impl TryFrom<Description> for KnownCameraControls {
type Error = NokhwaError;
fn try_from(value: Description) -> Result<Self, Self::Error> {
@@ -786,6 +799,7 @@ impl Display for KnownCameraControlFlag {
/// NOTE: Assume the values for `min` and `max` as **non-inclusive**!.
/// E.g. if the [`CameraControl`] says `min` is 100, the minimum is actually 101.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CameraControl {
control: KnownCameraControls,
min: i32,
@@ -1019,6 +1033,7 @@ impl Ord for CameraControl {
/// - `Network` - Uses `OpenCV` to capture from an IP.
/// - `Browser` - Uses browser APIs to capture from a webcam.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CaptureAPIBackend {
Auto,
AVFoundation,
@@ -1032,7 +1047,7 @@ pub enum CaptureAPIBackend {
}
impl Display for CaptureAPIBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let self_str = format!("{:?}", self);
write!(f, "{}", self_str)
}