mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
new format request type, quartar way wasm cam impl
This commit is contained in:
+6
-6
@@ -29,7 +29,8 @@ input-native = ["input-avfoundation", "input-v4l", "input-msmf"]
|
||||
# Re-enable it once soundness has been proven + mozjpeg is updated to 0.9.x
|
||||
# input-uvc = ["uvc", "uvc/vendor", "usb_enumeration", "lazy_static"]
|
||||
input-opencv = ["opencv", "opencv/rgb", "rgb", "nokhwa-core/opencv-mat"]
|
||||
input-jscam = ["web-sys", "js-sys", "wasm-bindgen-futures", "wasm-bindgen", "wasm-rs-async-executor", "output-async"]
|
||||
# FIXME: Change me back to web-sys being optional! People will be mad otherwise peg!
|
||||
input-jscam = [ "wasm-bindgen-futures", "wasm-rs-async-executor", "output-async"]
|
||||
output-wgpu = ["wgpu", "nokhwa-core/wgpu-types"]
|
||||
#output-wasm = ["input-jscam"]
|
||||
output-threaded = []
|
||||
@@ -65,11 +66,11 @@ version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.wgpu]
|
||||
version = "0.16"
|
||||
version = "0.17"
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
version = "0.82"
|
||||
version = "0.84"
|
||||
default-features = false
|
||||
features = ["videoio"]
|
||||
optional = true
|
||||
@@ -97,6 +98,7 @@ optional = true
|
||||
version = "1.7"
|
||||
optional = true
|
||||
|
||||
# TODO: Change me back!
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = [
|
||||
@@ -116,15 +118,13 @@ features = [
|
||||
"Plugin", "PluginArray",
|
||||
"Window"
|
||||
]
|
||||
optional = true
|
||||
|
||||
# FIXME: Change me back! Pls! REMEMBER PEG!
|
||||
[dependencies.js-sys]
|
||||
version = "0.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.wasm-bindgen-futures]
|
||||
version = "0.4"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod internal {
|
||||
use nokhwa_core::format_filter::FormatFilter;
|
||||
use nokhwa_core::format_request::FormatFilter;
|
||||
use nokhwa_core::{
|
||||
buffer::Buffer,
|
||||
error::NokhwaError,
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
use crate::frame_format::SourceFrameFormat;
|
||||
use crate::{
|
||||
frame_format::FrameFormat,
|
||||
types::{ApiBackend, CameraFormat, Resolution},
|
||||
};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
/// Tells the init function what camera format to pick.
|
||||
/// - `AbsoluteHighestResolution`: Pick the highest [`Resolution`], then pick the highest frame rate of those provided.
|
||||
/// - `AbsoluteHighestFrameRate`: Pick the highest frame rate, then the highest [`Resolution`].
|
||||
/// - `HighestResolution(Resolution)`: Pick the highest [`Resolution`] for the given framerate.
|
||||
/// - `HighestFrameRate(u32)`: Pick the highest frame rate for the given [`Resolution`].
|
||||
/// - `Exact`: Pick the exact [`CameraFormat`] provided.
|
||||
/// - `Closest`: Pick the closest [`CameraFormat`] provided in order of [`Resolution`], and FPS.
|
||||
/// - `ClosestGreater`: Pick the closest [`CameraFormat`] provided in order of [`Resolution`], and FPS. The returned format's [`Resolution`] **and** FPS will be **greater than or equal to** the provided [`CameraFormat`]
|
||||
/// - `ClosestLess`: Pick the closest [`CameraFormat`] provided in order of [`Resolution`], and FPS.The returned format's [`Resolution`] **and** FPS will be **less than or equal to** the provided [`CameraFormat`]
|
||||
/// - `None`: Pick a random [`CameraFormat`]
|
||||
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum RequestedFormatType {
|
||||
AbsoluteHighestResolution,
|
||||
AbsoluteHighestFrameRate,
|
||||
HighestResolution(u32),
|
||||
HighestFrameRate(Resolution),
|
||||
Exact(CameraFormat),
|
||||
ClosestGreater(CameraFormat),
|
||||
ClosestLess(CameraFormat),
|
||||
Closest(CameraFormat),
|
||||
None,
|
||||
}
|
||||
|
||||
// TODO: Format Filter Builder to provide more interactive API for fulfillment of formats.
|
||||
|
||||
/// How you get your [`FrameFormat`] from the
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FormatFilter {
|
||||
filter_pref: RequestedFormatType,
|
||||
fcc_primary: BTreeSet<FrameFormat>,
|
||||
fcc_platform: BTreeMap<ApiBackend, BTreeSet<u128>>,
|
||||
|
||||
}
|
||||
|
||||
impl FormatFilter {
|
||||
pub fn new(fmt_type: RequestedFormatType) -> Self {
|
||||
Self {
|
||||
filter_pref: fmt_type,
|
||||
fcc_primary: Default::default(),
|
||||
fcc_platform: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_allowed_frame_format(&mut self, frame_format: FrameFormat) {
|
||||
self.fcc_primary.insert(frame_format);
|
||||
}
|
||||
|
||||
pub fn add_allowed_frame_format_many(&mut self, frame_formats: impl AsRef<[FrameFormat]>) {
|
||||
self.fcc_primary.extend(frame_formats.as_ref().iter());
|
||||
}
|
||||
|
||||
pub fn add_allowed_platform_specific(&mut self, platform: ApiBackend, frame_format: u128) {
|
||||
match self.fcc_platform.get_mut(&platform) {
|
||||
Some(fccs) => {
|
||||
fccs.insert(frame_format);
|
||||
}
|
||||
None => {
|
||||
self.fcc_platform
|
||||
.insert(platform, BTreeSet::from([frame_format]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_allowed_platform_specific_many(
|
||||
&mut self,
|
||||
platform_specifics: impl AsRef<[(ApiBackend, u128)]>,
|
||||
) {
|
||||
for (platform, frame_format) in platform_specifics.as_ref().into_iter() {
|
||||
match self.fcc_platform.get_mut(&platform) {
|
||||
Some(fccs) => {
|
||||
fccs.insert(*frame_format);
|
||||
}
|
||||
None => {
|
||||
self.fcc_platform
|
||||
.insert(*platform, BTreeSet::from([*frame_format]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_allowed_frame_format(mut self, frame_format: FrameFormat) -> Self {
|
||||
self.fcc_primary.insert(frame_format);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allowed_frame_format_many(
|
||||
mut self,
|
||||
frame_formats: impl AsRef<[FrameFormat]>,
|
||||
) -> Self {
|
||||
self.fcc_primary.extend(frame_formats.as_ref().iter());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allowed_platform_specific(
|
||||
mut self,
|
||||
platform: ApiBackend,
|
||||
frame_format: u128,
|
||||
) -> Self {
|
||||
self.add_allowed_platform_specific(platform, frame_format);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allowed_platform_specific_many(
|
||||
mut self,
|
||||
platform_specifics: impl AsRef<[(ApiBackend, u128)]>,
|
||||
) -> Self {
|
||||
self.add_allowed_platform_specific_many(platform_specifics);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FormatFilter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
filter_pref: RequestedFormatType::Closest(CameraFormat::new(
|
||||
Resolution::new(640, 480),
|
||||
FrameFormat::Yuv422.into(),
|
||||
30,
|
||||
)),
|
||||
fcc_primary: BTreeSet::from([FrameFormat::Yuv422]),
|
||||
fcc_platform: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_fulfill(
|
||||
sources: impl AsRef<[CameraFormat]>,
|
||||
filter: FormatFilter,
|
||||
) -> Option<CameraFormat> {
|
||||
let mut sources = sources
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
.filter(|cam_filter| match cam_filter.format() {
|
||||
SourceFrameFormat::FrameFormat(fmt) => filter.fcc_primary.contains(&fmt),
|
||||
SourceFrameFormat::PlatformSpecific(plat) => filter
|
||||
.fcc_platform
|
||||
.get(&plat.backend())
|
||||
.map(|x| x.contains(&plat.format()))
|
||||
.unwrap_or(false),
|
||||
});
|
||||
|
||||
match filter.filter_pref {
|
||||
RequestedFormatType::AbsoluteHighestResolution => {
|
||||
let mut sources = sources.collect::<Vec<&CameraFormat>>();
|
||||
sources.sort_by(|a, b| a.resolution().cmp(&b.resolution()));
|
||||
sources.last().copied().copied()
|
||||
}
|
||||
RequestedFormatType::AbsoluteHighestFrameRate => {
|
||||
let mut sources = sources.collect::<Vec<&CameraFormat>>();
|
||||
sources.sort_by(|a, b| a.frame_rate().cmp(&b.frame_rate()));
|
||||
sources.last().copied().copied()
|
||||
}
|
||||
RequestedFormatType::HighestResolution(filter_fps) => {
|
||||
let mut sources = sources
|
||||
.filter(|format| format.frame_rate() == filter_fps)
|
||||
.collect::<Vec<&CameraFormat>>();
|
||||
sources.sort();
|
||||
sources.last().copied().copied()
|
||||
}
|
||||
RequestedFormatType::HighestFrameRate(filter_res) => {
|
||||
let mut sources = sources
|
||||
.filter(|format| format.resolution() == filter_res)
|
||||
.collect::<Vec<&CameraFormat>>();
|
||||
sources.sort();
|
||||
sources.last().copied().copied()
|
||||
}
|
||||
RequestedFormatType::Exact(exact) => {
|
||||
sources.filter(|format| format == &&exact).last().copied()
|
||||
}
|
||||
RequestedFormatType::Closest(closest) => {
|
||||
let mut sources = sources
|
||||
.map(|format| {
|
||||
let dist = distance_3d_camerafmt_relative(closest, *format);
|
||||
(dist, *format)
|
||||
})
|
||||
.collect::<Vec<(f64, CameraFormat)>>();
|
||||
sources.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
sources.first().copied().map(|(_, cf)| cf)
|
||||
}
|
||||
RequestedFormatType::ClosestGreater(closest) => {
|
||||
let mut sources = sources
|
||||
.filter(|format| {
|
||||
format.resolution() >= closest.resolution()
|
||||
&& format.frame_rate() >= closest.frame_rate()
|
||||
})
|
||||
.map(|format| {
|
||||
let dist = distance_3d_camerafmt_relative(closest, *format);
|
||||
(dist, *format)
|
||||
})
|
||||
.collect::<Vec<(f64, CameraFormat)>>();
|
||||
sources.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
sources.first().copied().map(|(_, cf)| cf)
|
||||
}
|
||||
RequestedFormatType::ClosestLess(closest) => {
|
||||
let mut sources = sources
|
||||
.filter(|format| {
|
||||
format.resolution() <= closest.resolution()
|
||||
&& format.frame_rate() <= closest.frame_rate()
|
||||
})
|
||||
.map(|format| {
|
||||
let dist = distance_3d_camerafmt_relative(closest, *format);
|
||||
(dist, *format)
|
||||
})
|
||||
.collect::<Vec<(f64, CameraFormat)>>();
|
||||
sources.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
sources.first().copied().map(|(_, cf)| cf)
|
||||
}
|
||||
RequestedFormatType::None => sources.nth(0).map(|x| *x),
|
||||
}
|
||||
}
|
||||
|
||||
fn distance_3d_camerafmt_relative(a: CameraFormat, b: CameraFormat) -> f64 {
|
||||
let res_x_diff = b.resolution().x() - a.resolution().x();
|
||||
let res_y_diff = b.resolution().y() - a.resolution().y();
|
||||
let fps_diff = b.frame_rate() - a.frame_rate();
|
||||
|
||||
let x = res_x_diff.pow(2) as f64;
|
||||
let y = res_y_diff.pow(2) as f64;
|
||||
let z = fps_diff.pow(2) as f64;
|
||||
|
||||
x + y + z
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
use crate::frame_format::SourceFrameFormat;
|
||||
use crate::types::Range;
|
||||
use crate::{
|
||||
frame_format::FrameFormat,
|
||||
types::{ApiBackend, CameraFormat, Resolution},
|
||||
};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum CustomFormatRequestType {
|
||||
HighestFPS,
|
||||
HighestResolution,
|
||||
Closest,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialOrd, PartialEq)]
|
||||
pub struct FormatRequest {
|
||||
resolution: Option<Range<Resolution>>,
|
||||
frame_rate: Option<Range<u32>>,
|
||||
frame_format: Option<Vec<FrameFormat>>,
|
||||
req_type: Option<CustomFormatRequestType>,
|
||||
}
|
||||
|
||||
impl FormatRequest {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_resolution(mut self, resolution: Resolution, exact: bool) -> Self {
|
||||
self.resolution = Some(resolution);
|
||||
self.resolution_exact = exact;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reset_resolution(mut self) -> Self {
|
||||
self.resolution = None;
|
||||
self.resolution_exact = false;
|
||||
self
|
||||
}
|
||||
pub fn with_frame_rate(mut self, frame_rate: u32, exact: bool) -> Self {
|
||||
self.frame_rate = Some(frame_rate);
|
||||
self.frame_rate_exact = exact;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_standard_frame_rate() {}
|
||||
|
||||
pub fn reset_frame_rate(mut self) -> Self {
|
||||
self.frame_rate = None;
|
||||
self.frame_rate_exact = false;
|
||||
self
|
||||
}
|
||||
pub fn with_frame_formats(mut self, frame_formats: Vec<FrameFormat>) -> Self {
|
||||
self.frame_format = Some(frame_formats);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_standard_frame_formats(mut self) -> Self {
|
||||
self.append_frame_formats(&mut vec![
|
||||
FrameFormat::MJpeg,
|
||||
FrameFormat::Rgb8,
|
||||
FrameFormat::Yuv422,
|
||||
FrameFormat::Nv12,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn push_frame_format(mut self, frame_format: FrameFormat) -> Self {
|
||||
match &mut self.frame_format {
|
||||
Some(ffs) => ffs.push(frame_format),
|
||||
None => self.frame_format = Some(vec![frame_format]),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn remove_frame_format(mut self, frame_format: FrameFormat) -> Self {
|
||||
if let Some(ffs) = &mut self.frame_format {
|
||||
if let Some(idx) = ffs.iter().position(frame_format) {
|
||||
ffs.remove(idx)
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn append_frame_formats(mut self, frame_formats: &mut Vec<FrameFormat>) -> Self {
|
||||
match &mut self.frame_format {
|
||||
Some(ffs) => ffs.append(frame_formats),
|
||||
None => self.frame_format = Some(frame_formats.clone()),
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reset_frame_formats(mut self) -> Self {
|
||||
self.frame_format = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_request_type(mut self, request_type: CustomFormatRequestType) -> Self {
|
||||
self.req_type = Some(request_type);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reset_request_type(mut self) -> Self {
|
||||
self.req_type = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_format_request(
|
||||
request: FormatRequest,
|
||||
availible_formats: Vec<CameraFormat>,
|
||||
) -> CameraFormat {
|
||||
// filter out by
|
||||
}
|
||||
@@ -21,8 +21,9 @@
|
||||
//! Core type definitions for `nokhwa`
|
||||
pub mod buffer;
|
||||
pub mod error;
|
||||
pub mod format_filter;
|
||||
pub mod format_request;
|
||||
pub mod frame_format;
|
||||
pub mod traits;
|
||||
pub mod types;
|
||||
pub mod decoder;
|
||||
pub mod utils;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
error::NokhwaError,
|
||||
format_filter::FormatFilter,
|
||||
format_request::FormatFilter,
|
||||
frame_format::SourceFrameFormat,
|
||||
types::{
|
||||
ApiBackend, CameraControl, CameraFormat, CameraInfo, ControlValueSetter,
|
||||
@@ -315,17 +315,6 @@ pub trait AsyncCaptureTrait: CaptureTrait {
|
||||
fourcc: SourceFrameFormat,
|
||||
) -> Result<(), NokhwaError>;
|
||||
|
||||
/// Gets the value of [`KnownCameraControl`].
|
||||
/// # Errors
|
||||
/// If the `control` is not supported or there is an error while getting the camera control values (e.g. unexpected value, too high, etc)
|
||||
/// this will error.
|
||||
async fn camera_control_async(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError>;
|
||||
|
||||
/// Gets the current supported list of [`KnownCameraControl`]
|
||||
/// # Errors
|
||||
/// If the list cannot be collected, this will error. This can be treated as a "nothing supported".
|
||||
async fn camera_controls_async(&self) -> Result<Vec<CameraControl>, NokhwaError>;
|
||||
|
||||
/// Sets the control to `control` in the camera.
|
||||
/// Usually, the pipeline is calling [`camera_control()`](CaptureTrait::camera_control), getting a camera control that way
|
||||
/// then calling [`value()`](CameraControl::value()) to get a [`ControlValueSetter`] and setting the value that way.
|
||||
|
||||
+93
-16
@@ -1,25 +1,101 @@
|
||||
use crate::{
|
||||
error::NokhwaError,
|
||||
format_filter::RequestedFormatType,
|
||||
frame_format::{FrameFormat, SourceFrameFormat},
|
||||
};
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
impl Default for RequestedFormatType {
|
||||
fn default() -> Self {
|
||||
RequestedFormatType::None
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub struct Range<T>
|
||||
where
|
||||
T: Copy + Clone + Debug + PartialOrd + PartialEq,
|
||||
{
|
||||
minimum: Option<T>,
|
||||
lower_inclusive: bool,
|
||||
maximum: Option<T>,
|
||||
upper_inclusive: bool,
|
||||
preferred: T,
|
||||
}
|
||||
|
||||
impl<T> Range<T>
|
||||
where
|
||||
T: Copy + Clone + Debug + PartialOrd + PartialEq,
|
||||
{
|
||||
pub fn new(preferred: T, min: Option<T>, max: Option<T>) -> Self {
|
||||
Self {
|
||||
minimum: min,
|
||||
lower_inclusive: true,
|
||||
maximum: max,
|
||||
upper_inclusive: false,
|
||||
preferred,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_inclusive(
|
||||
preferred: T,
|
||||
min: Option<T>,
|
||||
lower_inclusive: bool,
|
||||
max: Option<T>,
|
||||
upper_inclusive: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
minimum: min,
|
||||
lower_inclusive,
|
||||
maximum: max,
|
||||
upper_inclusive,
|
||||
preferred,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn does_fit(&self, item: T) -> bool {
|
||||
if item == self.preferred {
|
||||
true
|
||||
}
|
||||
|
||||
if let Some(min) = self.minimum {
|
||||
let test = if self.lower_inclusive {
|
||||
min >= item
|
||||
} else {
|
||||
min > item
|
||||
};
|
||||
if test {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max) = self.maximum {
|
||||
let test = if self.lower_inclusive {
|
||||
max <= item
|
||||
} else {
|
||||
max < item
|
||||
};
|
||||
if test {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RequestedFormatType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
impl<T> Default for Range<T>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Range {
|
||||
minimum: None,
|
||||
lower_inclusive: true,
|
||||
maximum: None,
|
||||
upper_inclusive: false,
|
||||
preferred: T::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +384,7 @@ impl CameraInfo {
|
||||
// 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: &str, description: &str, misc: &str, index: CameraIndex) -> Self {
|
||||
pub fn new(human_name: &str, description: &str, misc: &str, index: &CameraIndex) -> Self {
|
||||
CameraInfo {
|
||||
human_name: human_name.to_string(),
|
||||
description: description.to_string(),
|
||||
@@ -563,8 +639,8 @@ pub enum ControlValueDescription {
|
||||
},
|
||||
StringList {
|
||||
value: String,
|
||||
availible: Vec<String>
|
||||
}
|
||||
availible: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ControlValueDescription {
|
||||
@@ -598,7 +674,9 @@ impl ControlValueDescription {
|
||||
ControlValueDescription::RGB { value, .. } => {
|
||||
ControlValueSetter::RGB(value.0, value.1, value.2)
|
||||
}
|
||||
ControlValueDescription::StringList { value, .. } => ControlValueSetter::StringList(value.clone()),
|
||||
ControlValueDescription::StringList { value, .. } => {
|
||||
ControlValueSetter::StringList(value.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +774,7 @@ impl ControlValueDescription {
|
||||
},
|
||||
ControlValueDescription::StringList { value, availible } => {
|
||||
availible.contains(setter.as_str())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// match setter {
|
||||
@@ -864,7 +942,7 @@ impl Display for ControlValueDescription {
|
||||
}
|
||||
ControlValueDescription::StringList { value, availible } => {
|
||||
write!(f, "Current: {value}, Availible: {availible:?}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1113,7 +1191,7 @@ impl Display for ControlValueSetter {
|
||||
}
|
||||
ControlValueSetter::StringList(s) => {
|
||||
write!(f, "StringListValue: {s}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1346,7 +1424,7 @@ pub fn yuyv422_to_rgb(data: &[u8], rgba: bool) -> Result<Vec<u8>, NokhwaError> {
|
||||
/// If the stream is invalid Yuv422, or the destination buffer is not large enough, this will error.
|
||||
#[inline]
|
||||
pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> {
|
||||
let mut buf:Vec<u8> = Vec::new();
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
if data.len() % 4 != 0 {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: FrameFormat::Yuv422.into(),
|
||||
@@ -1381,7 +1459,6 @@ pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
use async_trait::async_trait;
|
||||
use js_sys::{Array, Object, Reflect, Map, Function};
|
||||
use js_sys::{Array, Function, Map, Reflect};
|
||||
use nokhwa_core::buffer::Buffer;
|
||||
use nokhwa_core::error::NokhwaError;
|
||||
use nokhwa_core::format_filter::FormatFilter;
|
||||
use nokhwa_core::format_request::FormatFilter;
|
||||
use nokhwa_core::frame_format::{FrameFormat, SourceFrameFormat};
|
||||
use nokhwa_core::traits::{AsyncCaptureTrait, Backend, CaptureTrait};
|
||||
use nokhwa_core::types::{
|
||||
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
|
||||
KnownCameraControl, Resolution, KnownCameraControlFlag,
|
||||
KnownCameraControl, KnownCameraControlFlag, Resolution,
|
||||
};
|
||||
use v4l::Control;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use nokhwa_core::utils::min_max_range;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, BTreeMap, HashSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{
|
||||
CanvasRenderingContext2d, Document, Element, MediaDevices, Navigator, OffscreenCanvas, Window, MediaStream, MediaStreamConstraints, HtmlCanvasElement, MediaDeviceInfo, MediaDeviceKind, MediaStreamTrack, MediaTrackSettings,
|
||||
CanvasRenderingContext2d, Document, Element, HtmlCanvasElement, MediaDeviceInfo,
|
||||
MediaDeviceKind, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack,
|
||||
MediaTrackSettings, Navigator, OffscreenCanvas, Window,
|
||||
};
|
||||
|
||||
|
||||
macro_rules! jsv {
|
||||
($value:expr) => {{
|
||||
JsValue::from($value)
|
||||
@@ -180,16 +181,17 @@ fn control_to_str(control: KnownCameraControl) -> &'static str {
|
||||
KnownCameraControl::Exposure => "exposureMode",
|
||||
KnownCameraControl::Iris => "focusDistance",
|
||||
KnownCameraControl::Focus => "focusMode",
|
||||
KnownCameraControl::Other(u) => {
|
||||
match u {
|
||||
8 => "aspectRatio",
|
||||
16 => "facingMode",
|
||||
32 => "resizeMode",
|
||||
64 => "attachedCanvasMode",
|
||||
128 => "pointsOfInterest",
|
||||
8192 => "torch",
|
||||
_ => "",
|
||||
}
|
||||
KnownCameraControl::Other(u) => match u {
|
||||
0 => "frameRate",
|
||||
1 => "width",
|
||||
2 => "height",
|
||||
8 => "aspectRatio",
|
||||
16 => "facingMode",
|
||||
32 => "resizeMode",
|
||||
64 => "attachedCanvasMode",
|
||||
128 => "pointsOfInterest",
|
||||
8192 => "torch",
|
||||
_ => "",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -233,7 +235,7 @@ impl AsRef<str> for JSCameraMeteringMode {
|
||||
JSCameraMeteringMode::Continuous => "continuous",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<JsValue> for JSCameraMeteringMode {
|
||||
fn into(self) -> JsValue {
|
||||
@@ -262,12 +264,10 @@ pub struct BrowserCamera {
|
||||
info: CameraInfo,
|
||||
format: CameraFormat,
|
||||
media_stream: MediaStream,
|
||||
track: MediaStreamTrack,
|
||||
init: bool,
|
||||
custom_controls: HashMap<u128, CameraControl>,
|
||||
controls: HashMap<KnownCameraControl, CameraControl>,
|
||||
canvas_attachment: Option<String>,
|
||||
supported_controls: HashSet<KnownCameraControl>,
|
||||
cavnas: Option<CanvasType>,
|
||||
cavnas: Option<String>,
|
||||
context: Option<CanvasRenderingContext2d>,
|
||||
}
|
||||
|
||||
@@ -285,10 +285,9 @@ impl BrowserCamera {
|
||||
video_constraints.set(&JsValue::from_str("tilt"), &JsValue::TRUE);
|
||||
video_constraints.set(&JsValue::from_str("zoom"), &JsValue::TRUE);
|
||||
|
||||
let stream: MediaStream = match media_devices.get_user_media_with_constraints(&MediaStreamConstraints::new().video(
|
||||
&video_constraints
|
||||
))
|
||||
{
|
||||
let stream: MediaStream = match media_devices.get_user_media_with_constraints(
|
||||
&MediaStreamConstraints::new().video(&video_constraints),
|
||||
) {
|
||||
Ok(promise) => {
|
||||
let future = JsFuture::from(promise);
|
||||
match future.await {
|
||||
@@ -298,21 +297,23 @@ impl BrowserCamera {
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::OpenDeviceError(
|
||||
"MediaDevicesGetUserMediaJsFuture".to_string(), format!("{why:?}"),
|
||||
"MediaDevicesGetUserMediaJsFuture".to_string(),
|
||||
format!("{why:?}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::OpenDeviceError(
|
||||
"MediaDevicesGetUserMedia".to_string(), format!("{why:?}"),
|
||||
"MediaDevicesGetUserMedia".to_string(),
|
||||
format!("{why:?}"),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let media_info = match media_devices.enumerate_devices() {
|
||||
Ok(i) => {
|
||||
let future = JsFuture::from(promise);
|
||||
let future = JsFuture::from(i);
|
||||
match future.await {
|
||||
Ok(devs) => {
|
||||
let arr = Array::from(&devs);
|
||||
@@ -321,46 +322,69 @@ impl BrowserCamera {
|
||||
let dr = arr.get(i as u32);
|
||||
|
||||
if dr == JsValue::UNDEFINED {
|
||||
return Err(NokhwaError::StructureError { structure: "MediaDeviceInfo".to_string(), error: "undefined".to_string() })
|
||||
return Err(NokhwaError::StructureError {
|
||||
structure: "MediaDeviceInfo".to_string(),
|
||||
error: "undefined".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
MediaDeviceInfo::from(dr)
|
||||
}
|
||||
CameraIndex::String(s) => {
|
||||
match arr.iter().map(MediaDeviceInfo::from)
|
||||
.filter(|mdi| {
|
||||
mdi.device_id() == s
|
||||
}).nth(0) {
|
||||
match arr
|
||||
.iter()
|
||||
.map(MediaDeviceInfo::from)
|
||||
.filter(|mdi| mdi.device_id() == s)
|
||||
.nth(0)
|
||||
{
|
||||
Some(i) => i,
|
||||
None => return Err(NokhwaError::StructureError { structure: "MediaDeviceInfo".to_string(), error: "no id".to_string() })
|
||||
|
||||
None => {
|
||||
return Err(NokhwaError::StructureError {
|
||||
structure: "MediaDeviceInfo".to_string(),
|
||||
error: "no id".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::StructureError { structure: "MediaDeviceInfo Enumerate Devices Promise".to_string(), error: format!("{why:?}") })
|
||||
return Err(NokhwaError::StructureError {
|
||||
structure: "MediaDeviceInfo Enumerate Devices Promise".to_string(),
|
||||
error: format!("{why:?}"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError { property: "MediaDeviceInfo".to_string(), error: format!("{why:?}") })
|
||||
},
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "MediaDeviceInfo".to_string(),
|
||||
error: format!("{why:?}"),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let info = CameraInfo::new(&media_info.label(), media_info.kind().to_string(), &format!("{}:{}", media_info.group_id().to_string(), media_info.device_id().to_string()),index);
|
||||
let track = stream.get_video_tracks().iter().nth(0).map(|x| element_cast(, name));
|
||||
Ok(BrowserCamera {
|
||||
index: index.clone(),
|
||||
info, format: CameraFormat::default(),
|
||||
init: false,
|
||||
cavnas: None,
|
||||
context: None,
|
||||
media_stream: stream,
|
||||
controls: HashMap::new(),
|
||||
custom_controls: HashMap::new(),
|
||||
supported_controls: HashSet::new(),
|
||||
track: todo!(), })
|
||||
let info = CameraInfo::new(
|
||||
&media_info.label(),
|
||||
&*media_info.kind().to_string(),
|
||||
&format!(
|
||||
"{}:{}",
|
||||
media_info.group_id().to_string(),
|
||||
media_info.device_id().to_string()
|
||||
),
|
||||
index,
|
||||
);
|
||||
Ok(BrowserCamera {
|
||||
index: index.clone(),
|
||||
info,
|
||||
format: CameraFormat::default(),
|
||||
init: false,
|
||||
cavnas: None,
|
||||
context: None,
|
||||
media_stream: stream,
|
||||
supported_controls: HashSet::new(),
|
||||
canvas_attachment: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,9 +393,7 @@ impl Backend for BrowserCamera {
|
||||
}
|
||||
|
||||
impl CaptureTrait for BrowserCamera {
|
||||
fn init(&mut self) -> Result<(), NokhwaError> {
|
||||
|
||||
}
|
||||
fn init(&mut self) -> Result<(), NokhwaError> {}
|
||||
|
||||
fn init_with_format(&mut self, format: FormatFilter) -> Result<CameraFormat, NokhwaError> {
|
||||
self.init()?;
|
||||
@@ -436,7 +458,191 @@ impl CaptureTrait for BrowserCamera {
|
||||
}
|
||||
|
||||
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
||||
|
||||
// TODO: Werid Controls like framerate and attach support
|
||||
|
||||
if let KnownCameraControl::Other(custom) = control {
|
||||
if custom == 64 {
|
||||
// attached canvas mode
|
||||
return Ok(CameraControl::new(
|
||||
KnownCameraControl::Other(64),
|
||||
"AttachedCanvasMode".to_string(),
|
||||
nokhwa_core::types::ControlValueDescription::String {
|
||||
value: self.canvas_attachment,
|
||||
default: None,
|
||||
},
|
||||
vec![],
|
||||
active,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let cam_str = control_to_str(contorl);
|
||||
let capabilities_fn = match unsafe { Reflect::get(&self.track, "getCapabilities") } {
|
||||
Ok(v) => match v.dyn_ref::<Function>() {
|
||||
Some(fx) => fx,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "getCapabilities".to_string(),
|
||||
error: "getCapabilities is not a function!".to_string(),
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "getCapabilities".to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let capabilities = match capabilities_fn.call0(&self.track) {
|
||||
Ok(c) => c,
|
||||
Err(v) => NokhwaError::GetPropertyError {
|
||||
property: "getCapabilities".to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
}, // ok i guess, thanks vscode
|
||||
};
|
||||
let settings = self.track.get_settings();
|
||||
let constraint_value = match unsafe { Reflect::get(&constraints, cam_str) } {
|
||||
Ok(v) => v,
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: cam_str.to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let setting_value = match unsafe { Reflect::get(&settings, cam_str) } {
|
||||
Ok(v) => v,
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: cam_str.to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// setting range!
|
||||
if unsafe { Reflect::get(&setting_value, "min").is_ok() } {
|
||||
let min = match unsafe { Reflect::get(&setting_value, "min") } {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "min".to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let min = match min {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "min".to_string(),
|
||||
error: "Not a f64! Did the API change?".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let max = match unsafe { Reflect::get(&setting_value, "max") } {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "max".to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let max = match max {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "max".to_string(),
|
||||
error: "Not a f64! Did the API change?".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let step = match unsafe { Reflect::get(&setting_value, "step") } {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "step".to_string(),
|
||||
error: why.as_string().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
let step = match step {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "step".to_string(),
|
||||
error: "Not a f64! Did the API change?".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let value = match constraint_value.as_f64() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "value".to_string(),
|
||||
error: "Not a f64! Did the API change?".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(CameraControl::new(
|
||||
control,
|
||||
cam_str.to_string(),
|
||||
nokhwa_core::types::ControlValueDescription::FloatRange {
|
||||
min,
|
||||
max,
|
||||
value,
|
||||
step,
|
||||
default: f64::default(),
|
||||
},
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
}
|
||||
// im a sequence<DOMString>
|
||||
else if setting_value.is_array() {
|
||||
let availible = Array::from(&setting_value)
|
||||
.iter()
|
||||
.map(|x| match x.as_string() {
|
||||
Some(v) => format!("{v}:"),
|
||||
None => String::new(),
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
let value = match constraint_value.as_string() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: "value".to_string(),
|
||||
error: "Not a String! Did the API change?".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
return Ok(CameraControl::new(
|
||||
control,
|
||||
cam_str.to_string(),
|
||||
nokhwa_core::types::ControlValueDescription::StringList { value, availible },
|
||||
vec![],
|
||||
true,
|
||||
));
|
||||
}
|
||||
// nope im a bool
|
||||
else {
|
||||
let is_truthy = constraint_value.is_truthy();
|
||||
return Ok(CameraControl::new(
|
||||
control,
|
||||
cam_str.to_string(),
|
||||
nokhwa_core::types::ControlValueDescription::Boolean {
|
||||
value: is_truthy,
|
||||
default: false,
|
||||
},
|
||||
flag,
|
||||
true,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
@@ -479,7 +685,6 @@ impl AsyncCaptureTrait for BrowserCamera {
|
||||
let media_devices = media_devices(&window.navigator())?;
|
||||
|
||||
// request permission for camera
|
||||
|
||||
|
||||
// first populate supported controls and see if we have our required controls
|
||||
// required: FPS, Resolution (width + height)
|
||||
@@ -490,149 +695,245 @@ impl AsyncCaptureTrait for BrowserCamera {
|
||||
let mut supported_constraints = HashSet::new();
|
||||
|
||||
let defaults_satisfied = {
|
||||
Reflect::get(&browser_constraints, "frameRate".into()).map(|x| x.is_truthy()).unwrap_or(false) && Reflect::get(&browser_constraints, "width".into()).map(|x| x.is_truthy()).unwrap_or(false) && Reflect::get(&browser_constraints, "height".into()).map(|x| x.is_truthy()).unwrap_or(false)
|
||||
unsafe { Reflect::get(&browser_constraints, "frameRate".into()) }
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
&& unsafe { Reflect::get(&browser_constraints, "width".into()) }
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
&& unsafe { Reflect::get(&browser_constraints, "height".into()) }
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
if !defaults_satisfied {
|
||||
return Err(NokhwaError::InitializeError { backend: ApiBackend::Browser, error: "Your browser does not support the required constraints! (frameRate, width, height)".to_string() });
|
||||
}
|
||||
|
||||
// STAY ~~WHITE~~ CLEAN WITH US! JOIN ~~WHITE~~ MATCH EXPRESSION SOCIETY!
|
||||
|
||||
// aspectRatio
|
||||
if Reflect::get(&browser_constraints, "aspectRatio".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Other(8));
|
||||
}
|
||||
// SAFETY: /shurg
|
||||
unsafe {
|
||||
// aspectRatio
|
||||
if Reflect::get(&browser_constraints, "aspectRatio".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Other(8));
|
||||
}
|
||||
|
||||
// facingMode
|
||||
if Reflect::get(&browser_constraints, "facingMode".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Other(16));
|
||||
}
|
||||
// facingMode
|
||||
if Reflect::get(&browser_constraints, "facingMode".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Other(16));
|
||||
}
|
||||
|
||||
// resizeMode
|
||||
if Reflect::get(&browser_constraints, "resizeMode".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Other(32));
|
||||
}
|
||||
// resizeMode
|
||||
if Reflect::get(&browser_constraints, "resizeMode".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Other(32));
|
||||
}
|
||||
|
||||
// attachedCanvasMode
|
||||
supported_constraints.insert(KnownCameraControl::Other(64));
|
||||
// attachedCanvasMode
|
||||
supported_constraints.insert(KnownCameraControl::Other(64));
|
||||
|
||||
// whiteBalanceMode
|
||||
if Reflect::get(&browser_constraints, "whiteBalanceMode".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::WhiteBalance);
|
||||
}
|
||||
// whiteBalanceMode
|
||||
if Reflect::get(&browser_constraints, "whiteBalanceMode".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::WhiteBalance);
|
||||
}
|
||||
|
||||
// exposureMode
|
||||
if Reflect::get(&browser_constraints, "exposureMode".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Exposure);
|
||||
}
|
||||
// exposureMode
|
||||
if Reflect::get(&browser_constraints, "exposureMode".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Exposure);
|
||||
}
|
||||
|
||||
// focusMode
|
||||
if Reflect::get(&browser_constraints, "focusMode".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Focus);
|
||||
}
|
||||
// focusMode
|
||||
if Reflect::get(&browser_constraints, "focusMode".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Focus);
|
||||
}
|
||||
|
||||
// pointsOfInterest
|
||||
if Reflect::get(&browser_constraints, "pointsOfInterest".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Other(128));
|
||||
}
|
||||
// pointsOfInterest
|
||||
if Reflect::get(&browser_constraints, "pointsOfInterest".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Other(128));
|
||||
}
|
||||
|
||||
// exposureCompensation
|
||||
if Reflect::get(&browser_constraints, "exposureCompensation".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::BacklightComp);
|
||||
}
|
||||
// exposureCompensation
|
||||
if Reflect::get(&browser_constraints, "exposureCompensation".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::BacklightComp);
|
||||
}
|
||||
|
||||
// exposureTime
|
||||
if Reflect::get(&browser_constraints, "exposureTime".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Gamma);
|
||||
}
|
||||
// exposureTime
|
||||
if Reflect::get(&browser_constraints, "exposureTime".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Gamma);
|
||||
}
|
||||
|
||||
// colorTemprature
|
||||
if Reflect::get(&browser_constraints, "colorTemprature".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Hue);
|
||||
}
|
||||
// colorTemprature
|
||||
if Reflect::get(&browser_constraints, "colorTemprature".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Hue);
|
||||
}
|
||||
|
||||
// iso
|
||||
if Reflect::get(&browser_constraints, "iso".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Gain);
|
||||
}
|
||||
// iso
|
||||
if Reflect::get(&browser_constraints, "iso".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Gain);
|
||||
}
|
||||
|
||||
// brightness
|
||||
if Reflect::get(&browser_constraints, "brightness".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Brightness);
|
||||
}
|
||||
// brightness
|
||||
if Reflect::get(&browser_constraints, "brightness".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Brightness);
|
||||
}
|
||||
|
||||
// contrast
|
||||
if Reflect::get(&browser_constraints, "contrast".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Contrast);
|
||||
}
|
||||
// contrast
|
||||
if Reflect::get(&browser_constraints, "contrast".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Contrast);
|
||||
}
|
||||
|
||||
// pan
|
||||
if Reflect::get(&browser_constraints, "pan".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Pan);
|
||||
}
|
||||
// pan
|
||||
if Reflect::get(&browser_constraints, "pan".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Pan);
|
||||
}
|
||||
|
||||
// saturation
|
||||
if Reflect::get(&browser_constraints, "saturation".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Saturation);
|
||||
}
|
||||
// saturation
|
||||
if Reflect::get(&browser_constraints, "saturation".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Saturation);
|
||||
}
|
||||
|
||||
// sharpness
|
||||
if Reflect::get(&browser_constraints, "sharpness".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Sharpness);
|
||||
}
|
||||
// sharpness
|
||||
if Reflect::get(&browser_constraints, "sharpness".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Sharpness);
|
||||
}
|
||||
|
||||
// focusDistance
|
||||
if Reflect::get(&browser_constraints, "focusDistance".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Iris);
|
||||
}
|
||||
// focusDistance
|
||||
if Reflect::get(&browser_constraints, "focusDistance".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Iris);
|
||||
}
|
||||
|
||||
// tilt
|
||||
if Reflect::get(&browser_constraints, "tilt".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Tilt);
|
||||
}
|
||||
// tilt
|
||||
if Reflect::get(&browser_constraints, "tilt".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Tilt);
|
||||
}
|
||||
|
||||
// zoom
|
||||
if Reflect::get(&browser_constraints, "zoom".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Zoom);
|
||||
}
|
||||
// zoom
|
||||
if Reflect::get(&browser_constraints, "zoom".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Zoom);
|
||||
}
|
||||
|
||||
// torch
|
||||
if Reflect::get(&browser_constraints, "torch".into()).map(|x| x.is_truthy()).unwrap_or(false) {
|
||||
supported_constraints.insert(KnownCameraControl::Other(8192));
|
||||
// torch
|
||||
if Reflect::get(&browser_constraints, "torch".into())
|
||||
.map(|x| x.is_truthy())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
supported_constraints.insert(KnownCameraControl::Other(8192));
|
||||
}
|
||||
}
|
||||
|
||||
// PUT ME INTO THE CHARLOTTE VESSEL COACH I'LL PROVE FREE WILL IS REAL
|
||||
|
||||
self.supported_controls = supported_constraints;
|
||||
self.init = true;
|
||||
|
||||
// get values for supported controls
|
||||
|
||||
for control in self.supported_controls {
|
||||
match control {
|
||||
KnownCameraControl::Brightness => {
|
||||
|
||||
}
|
||||
KnownCameraControl::Contrast => todo!(),
|
||||
KnownCameraControl::Hue => todo!(),
|
||||
KnownCameraControl::Saturation => todo!(),
|
||||
KnownCameraControl::Sharpness => todo!(),
|
||||
KnownCameraControl::Gamma => todo!(),
|
||||
KnownCameraControl::WhiteBalance => todo!(),
|
||||
KnownCameraControl::BacklightComp => todo!(),
|
||||
KnownCameraControl::Gain => todo!(),
|
||||
KnownCameraControl::Pan => todo!(),
|
||||
KnownCameraControl::Tilt => todo!(),
|
||||
KnownCameraControl::Zoom => todo!(),
|
||||
KnownCameraControl::Exposure => todo!(),
|
||||
KnownCameraControl::Iris => todo!(),
|
||||
KnownCameraControl::Focus => todo!(),
|
||||
KnownCameraControl::Other(_) => todo!(),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_with_format_async(&mut self, format: FormatFilter) -> Result<CameraFormat, NokhwaError> {
|
||||
todo!()
|
||||
async fn init_with_format_async(
|
||||
&mut self,
|
||||
format: FormatFilter,
|
||||
) -> Result<CameraFormat, NokhwaError> {
|
||||
self.init_async()?;
|
||||
|
||||
// now we need to get all formats possible
|
||||
|
||||
let frame_rates = match self
|
||||
.camera_control(KnownCameraControl::Other(0))?
|
||||
.description()
|
||||
{
|
||||
nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => {
|
||||
min_max_range(min, max, step)
|
||||
}
|
||||
_ => Err(NokhwaError::GetPropertyError {
|
||||
property: "frameRate".to_string(),
|
||||
error: "Bad FrameRate Type".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
let widths = match self
|
||||
.camera_control(KnownCameraControl::Other(1))?
|
||||
.description()
|
||||
{
|
||||
nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => {
|
||||
min_max_range(min, max, step)
|
||||
}
|
||||
_ => Err(NokhwaError::GetPropertyError {
|
||||
property: "width".to_string(),
|
||||
error: "Bad width Type".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
let heights = match self
|
||||
.camera_control(KnownCameraControl::Other(2))?
|
||||
.description()
|
||||
{
|
||||
nokhwa_core::types::ControlValueDescription::FloatRange { min, max, step, .. } => {
|
||||
min_max_range(min, max, step)
|
||||
}
|
||||
_ => Err(NokhwaError::GetPropertyError {
|
||||
property: "height".to_string(),
|
||||
error: "Bad height Type".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
async fn refresh_camera_format_async(&mut self) -> Result<(), NokhwaError> {
|
||||
@@ -643,7 +944,10 @@ impl AsyncCaptureTrait for BrowserCamera {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn compatible_list_by_resolution_async(&mut self, fourcc: SourceFrameFormat) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
|
||||
async fn compatible_list_by_resolution_async(
|
||||
&mut self,
|
||||
fourcc: SourceFrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -663,103 +967,18 @@ impl AsyncCaptureTrait for BrowserCamera {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn set_frame_format_async(&mut self, fourcc: SourceFrameFormat) -> Result<(), NokhwaError> {
|
||||
async fn set_frame_format_async(
|
||||
&mut self,
|
||||
fourcc: SourceFrameFormat,
|
||||
) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// TODO: Verify that constraint_value and setting_value are in the right place and order!
|
||||
async fn camera_control_async(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
||||
// TODO: Werid Controls like framerate and attach support
|
||||
let cam_str = control_to_str(contorl);
|
||||
let capabilities_fn = match Reflect::get(&self.track, "getCapabilities") {
|
||||
Ok(v) => match v.dyn_ref::<Function>() {
|
||||
Some(fx) => fx,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "getCapabilities".to_string(), error: "getCapabilities is not a function!".to_string() }),
|
||||
},
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: "getCapabilities".to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
let capabilities = match capabilities_fn.call0(&self.track) {
|
||||
Ok(c) => c,
|
||||
Err(V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH) => NokhwaError::GetPropertyError{ property: "getCapabilities".to_string(), error: why.as_string().unwrap_or_default() }, // ok i guess, thanks vscode
|
||||
};
|
||||
let settings = self.track.get_settings();
|
||||
let constraint_value = match Reflect::get(&constraints, cam_str) {
|
||||
Ok(v) => v,
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: cam_str.to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
let setting_value = match Reflect::get(&settings, cam_str) {
|
||||
Ok(v) => v,
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: cam_str.to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
|
||||
// setting range!
|
||||
if Reflect::get(&setting_value, "min").is_ok() {
|
||||
let min = match Reflect::get(&setting_value, "min") {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: "min".to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
let min = match min {
|
||||
Some(v) => v,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "min".to_string(), error: "Not a f64! Did the API change?".to_string() }),
|
||||
};
|
||||
let max = match Reflect::get(&setting_value, "max") {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: "max".to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
let max = match max {
|
||||
Some(v) => v,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "max".to_string(), error: "Not a f64! Did the API change?".to_string() }),
|
||||
};
|
||||
let step = match Reflect::get(&setting_value, "step") {
|
||||
Ok(v) => v.as_f64(),
|
||||
Err(why) => return Err(NokhwaError::GetPropertyError{ property: "step".to_string(), error: why.as_string().unwrap_or_default() }),
|
||||
};
|
||||
let step = match step {
|
||||
Some(v) => v,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "step".to_string(), error: "Not a f64! Did the API change?".to_string() }),
|
||||
};
|
||||
|
||||
let value = match constraint_value.as_f64() {
|
||||
Some(v) => v,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "value".to_string(), error: "Not a f64! Did the API change?".to_string() }),
|
||||
};
|
||||
|
||||
return Ok(
|
||||
CameraControl::new(control, cam_str.to_string(), nokhwa_core::types::ControlValueDescription::FloatRange { min, max, value, step, default: f64::default() }, vec![], true)
|
||||
)
|
||||
}
|
||||
// im a sequence<DOMString>
|
||||
else if setting_value.is_array() {
|
||||
let availible = Array::from(&setting_value).iter().map(|x| {
|
||||
match x.as_string() {
|
||||
Some(v) => format!("{v}:"),
|
||||
None => String::new(),
|
||||
}
|
||||
}).collect::<String>();
|
||||
|
||||
let value = match constraint_value.as_string() {
|
||||
Some(v) => v,
|
||||
None => return Err(NokhwaError::GetPropertyError{ property: "value".to_string(), error: "Not a String! Did the API change?".to_string() }),
|
||||
};
|
||||
return Ok(
|
||||
CameraControl::new(control, cam_str.to_string(), nokhwa_core::types::ControlValueDescription::StringList { value, availible }, vec![], true)
|
||||
)
|
||||
}
|
||||
// nope im a bool
|
||||
else {
|
||||
let is_truthy = constraint_value.is_truthy();
|
||||
return Ok(
|
||||
CameraControl::new(control, cam_str.to_string(), nokhwa_core::types::ControlValueDescription::Boolean { value: is_truthy, default: false }, flag, true)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async fn camera_controls_async(&self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn set_camera_control_async(&mut self, id: KnownCameraControl, value: ControlValueSetter) -> Result<(), NokhwaError> {
|
||||
async fn set_camera_control_async(
|
||||
&mut self,
|
||||
id: KnownCameraControl,
|
||||
value: ControlValueSetter,
|
||||
) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use nokhwa_core::format_filter::FormatFilter;
|
||||
use nokhwa_core::format_request::FormatFilter;
|
||||
use nokhwa_core::frame_format::SourceFrameFormat;
|
||||
use nokhwa_core::traits::Backend;
|
||||
use nokhwa_core::{
|
||||
|
||||
Reference in New Issue
Block a user