diff --git a/flake.nix b/flake.nix index 3a9278f..9358b86 100644 --- a/flake.nix +++ b/flake.nix @@ -87,6 +87,7 @@ gmp openapv svt-av1 + mkvtoolnix-cli ]); env.RUST_SRC_PATH = "${rustbin}/lib/rustlib/src/rust/library"; diff --git a/nokhwa-bindings-linux/Cargo.toml b/nokhwa-bindings-linux/Cargo.toml index d0da3da..99307ad 100644 --- a/nokhwa-bindings-linux/Cargo.toml +++ b/nokhwa-bindings-linux/Cargo.toml @@ -9,18 +9,10 @@ keywords = ["v4l", "v4l2", "linux", "nokhwa", "webcam"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -v4l2 = ["v4l", "v4l2-sys-mit"] -pw = ["pipewire"] -async = ["flume/async", "nokhwa-core/async"] [dependencies] v4l = { version = "0.14", features = ["v4l2"], optional = true } v4l2-sys-mit = { version = "0.3", optional = true } -flume = "0.11.1" - -[dependencies.pipewire] -version = "0.8" -optional = true [dependencies.nokhwa-core] version = "0.2" diff --git a/nokhwa-bindings-linux/src/lib.rs b/nokhwa-bindings-linux/src/lib.rs index fa7e2e7..8f050f0 100644 --- a/nokhwa-bindings-linux/src/lib.rs +++ b/nokhwa-bindings-linux/src/lib.rs @@ -13,7 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -pub mod pipewire; -#[cfg(feature = "v4l2")] pub mod v4l2; -mod v4l2r; diff --git a/nokhwa-bindings-linux/src/pipewire.rs b/nokhwa-bindings-linux/src/pipewire.rs deleted file mode 100644 index d0d0501..0000000 --- a/nokhwa-bindings-linux/src/pipewire.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO: todo, probably 0.12 diff --git a/nokhwa-bindings-linux/src/v4l2.rs b/nokhwa-bindings-linux/src/v4l2.rs index 7fb41b8..7fad87e 100644 --- a/nokhwa-bindings-linux/src/v4l2.rs +++ b/nokhwa-bindings-linux/src/v4l2.rs @@ -1,30 +1,27 @@ -use flume::{bounded, unbounded, Sender}; -use nokhwa_core::camera::{Camera, Capture, Setting}; +use nokhwa_core::camera::CameraTrait; use nokhwa_core::control::{ - ControlDescription, ControlFlags, ControlId, ControlValue, ControlValueDescriptor, Controls, - Orientation, + Control as NokhwaControl, ControlDescription, ControlFlags, ControlId, ControlValue, + ControlValueDescriptor, CustomControlId, Orientation, }; -use nokhwa_core::error::{NokhwaError, NokhwaResult}; -use nokhwa_core::frame_buffer::{CompactString, FrameBuffer, Metadata}; -use nokhwa_core::frame_format::FrameFormat; -use nokhwa_core::platform::{Backends, PlatformTrait}; +use nokhwa_core::error::{NokhwaError, NokhwaResult, StreamError}; +use nokhwa_core::frame_format::{CustomFrameFormat, FrameFormat}; +use nokhwa_core::metadata::{Metadata, MetadataTypes, Time}; +use nokhwa_core::platform::PlatformTrait; use nokhwa_core::ranges::Range; -use nokhwa_core::stream::{Event, StreamBounds, StreamConfiguration, StreamHandle}; -use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation, FrameRate, Resolution}; -use std::borrow::Cow; -use std::collections::hash_map::{Keys, Values}; +use nokhwa_core::stream::StreamTrait; +use nokhwa_core::types::{ + Backends, CameraFormat, CameraIndex, CameraInformation, FrameRate, QueriedCamera, Resolution, +}; use std::collections::{HashMap, HashSet}; -use std::num::NonZeroI32; -use std::sync::Arc; +use std::sync::atomic::AtomicBool; use std::thread::JoinHandle; use v4l::context::enum_devices; use v4l::control::{Description, Flags, MenuItem, Type, Value}; use v4l::frameinterval::FrameIntervalEnum; -use v4l::io::traits::OutputStream; +use v4l::io::traits::{CaptureStream, Stream}; use v4l::prelude::MmapStream; -use v4l::video::output::Parameters; -use v4l::video::Output; -use v4l::{Capabilities, Control, Device, Format, FourCC, Fraction, FrameInterval}; +use v4l::video::traits::Capture; +use v4l::{Capabilities, Device, FourCC, FrameInterval}; use v4l2_sys_mit::{ V4L2_CAMERA_ORIENTATION_BACK, V4L2_CAMERA_ORIENTATION_EXTERNAL, V4L2_CAMERA_ORIENTATION_FRONT, V4L2_CID_AUTO_EXPOSURE_BIAS, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_CID_AUTO_FOCUS_STATUS, @@ -37,7 +34,7 @@ use v4l2_sys_mit::{ V4L2_CID_ZOOM_CONTINUOUS, V4L2_CID_ZOOM_RELATIVE, }; -fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> CameraInformation { +fn index_capabilities_to_camera_info(capabilities: Capabilities) -> CameraInformation { let name = capabilities.card; let description = capabilities.driver; let misc = format!( @@ -49,34 +46,39 @@ fn index_capabilities_to_camera_info(index: u32, capabilities: Capabilities) -> capabilities.capabilities ); - CameraInformation::new(name, description, misc, CameraIndex::Index(index)) + CameraInformation::new(name, description, misc, None) } macro_rules! define_back_and_forth { - ( $($frame_format:expr => $fourcc:expr ,)+ ) => { + ( $($frame_format:path => $fourcc:literal ,)+ ) => { fn frame_format_to_fourcc(frame_format: FrameFormat) -> Result { match frame_format { $( $frame_format => Ok(FourCC::new($fourcc)), )+ - FrameFormat::Custom(def) => { - // if 4-7 is set (non-null) return an error. - if def[4..=7] != [0x00, 0x00, 0x00, 0x00] { - return Err(NokhwaError::ConversionError("Invalid: Custom bytes 4-7 are set (linux only uses 0-3)".to_string())) + FrameFormat::Custom(def) => { + if let CustomFrameFormat::FourCC(fcc) = def { + // if 4-7 is set (non-null) return an error. + Ok(FourCC { + repr: fcc, + }) + + } else { + return Err(NokhwaError::InvalidFrameFormat(frame_format, "Unsupported CustomFrameFormat".to_string())) + } + } + _ => { + return Err(NokhwaError::InvalidFrameFormat(frame_format, "Unsupported FrameFormat".to_string())) + } } - Ok(FourCC::new(&[def[0], def[1], def[2], def[3]])) - } - _ => { - return Err(NokhwaError::ConversionError("Unsupported FrameFormat".to_string())) - }} } fn fourcc_to_frame_format(four_cc: FourCC) -> FrameFormat { match &four_cc.repr { $( - $fourcc => $frame_format + $fourcc => $frame_format, )+ - custom => FrameFormat::Custom([ custom[0], custom[1], custom[2], custom[3], 0x00, 0x00, 0x00, 0x00 ]) + custom => FrameFormat::Custom(CustomFrameFormat::FourCC(*custom)) } } } @@ -85,51 +87,51 @@ macro_rules! define_back_and_forth { define_back_and_forth!( FrameFormat::H265 => b"HEVC", FrameFormat::H264 => b"H264", - FrameFormat::Avc1 => b"AVC1", + FrameFormat::AVC1 => b"AVC1", FrameFormat::H263 => b"H263", - FrameFormat::Av1 => b"AV1F", - FrameFormat::Mpeg1 => b"MPG1", - FrameFormat::Mpeg2 => b"MPG2", - FrameFormat::Mpeg4 => b"MPG4", - FrameFormat::MJpeg => b"MJPG", - FrameFormat::XVid => b"XVID", + FrameFormat::AV1 => b"AV1F", + FrameFormat::MPEG_1 => b"MPG1", + FrameFormat::MPEG_2 => b"MPG2", + FrameFormat::MPEG_4 => b"MPG4", + FrameFormat::MJPEG => b"MJPG", + FrameFormat::XviD => b"XVID", FrameFormat::VP8 => b"VP80", FrameFormat::VP9 => b"VP90", - FrameFormat::Ayuv444 => b"AYUV", - FrameFormat::Yuyv422 => b"YUYV", - FrameFormat::Uyvy422 => b"UYVY", - FrameFormat::Yvyu422 => b"YVYU", - FrameFormat::Yv12 => b"YV12", - FrameFormat::Nv12 => b"NV12", - FrameFormat::Nv21 => b"NV21", - FrameFormat::I420 => b"YU12", - FrameFormat::Yvu9 => b"YVU9", - FrameFormat::Luma8 => b"GREY", - FrameFormat::Luma16 => b"Y16 ", - FrameFormat::Depth16 => b"Z16 ", - FrameFormat::Rgb332 => b"RGB1", - FrameFormat::Rgb888 => b"RGB3", - FrameFormat::Bgr888 => b"BGR3", - FrameFormat::BgrA8888 => b"RA24", - FrameFormat::RgbA8888 => b"AB24", - FrameFormat::ARgb8888 => b"BA24", - FrameFormat::Bayer8 => b"BA81", - FrameFormat::Bayer16 => b"BYR2", + FrameFormat::Ayuv_32 => b"AYUV", + FrameFormat::Yuyv_4_2_2 => b"YUYV", + FrameFormat::Uyvy_4_2_2 => b"UYVY", + FrameFormat::Yvyu_4_2_2 => b"YVYU", + FrameFormat::NV12 => b"NV12", + FrameFormat::NV21 => b"NV21", + FrameFormat::NV16 => b"NV16", + FrameFormat::NV61 => b"NV61", + FrameFormat::NV24 => b"NV24", + FrameFormat::NV42 => b"NV42", + FrameFormat::Luma_8 => b"GREY", + FrameFormat::Luma_16 => b"Y16 ", + FrameFormat::Depth_16 => b"Z16 ", + FrameFormat::Rgb_3_3_2 => b"RGB1", + FrameFormat::Rgb_8_8_8 => b"RGB3", + FrameFormat::Bgr_8_8_8 => b"BGR3", + FrameFormat::Bgra_8_8_8_8 => b"RA24", + FrameFormat::Rgba_8_8_8_8 => b"AB24", + FrameFormat::Argb_8_8_8_8 => b"BA24", ); macro_rules! define_control_id_conv { - ( $($control_id:expr => $v4l_cid:expr ,)+ ) => { + ( $($control_id:path => $v4l_cid:ident ,)+ ) => { fn control_id_to_cid(control_id: ControlId) -> Result { match control_id { $( - $control_id => Ok($v4l_cid) + $control_id => Ok($v4l_cid), )+ - ControlId::PlatformSpecific(specific_id) => { - u32::try_from(specific_id).map_err(|why| { - NokhwaError::ConversionError("ID must be a u32".to_string()) - }) + ControlId::Custom(custom_id) => { + match custom_id { + CustomControlId::U32(specific) => Ok(specific), + _ => Err(NokhwaError::InvalidControlId(control_id, "expected u32 control id".to_string())) + } } - _ => Err(NokhwaError::ConversionError("Could not match ID".to_string()) + _ => Err(NokhwaError::InvalidControlId(control_id, "Could not match ID".to_string()) ) } } @@ -137,14 +139,15 @@ macro_rules! define_control_id_conv { fn control_id_to_cid_ref(control_id: &ControlId) -> Result { match control_id { $( - $control_id => Ok($v4l_cid) + $control_id => Ok($v4l_cid), )+ - ControlId::PlatformSpecific(specific_id) => { - u32::try_from(specific_id).map_err(|why| { - NokhwaError::ConversionError("ID must be a u32".to_string()) - }) + ControlId::Custom(specific_id) => { + match specific_id { + CustomControlId::U32(specific) => Ok(*specific), + _ => Err(NokhwaError::InvalidControlId(*control_id, "expected u32 control id".to_string())) + } } - _ => Err(NokhwaError::ConversionError("Could not match ID".to_string()) + _ => Err(NokhwaError::InvalidControlId(*control_id, "Could not match ID".to_string()) ) } } @@ -152,9 +155,9 @@ macro_rules! define_control_id_conv { fn cid_to_control_id(cid: u32) -> ControlId { match cid { $( - $v4l_cid => $control_id + $v4l_cid => $control_id, )+ - other_id => ControlId::PlatformSpecific(other_id as u64) + other_id => ControlId::Custom(other_id.into()) } } } @@ -224,7 +227,7 @@ fn flags(flags: Flags) -> HashSet { output_flags } -fn convert_description_to_ctrl_body(description: Description) -> Option { +fn convert_description_to_ctrl_body(description: &Description) -> Option { let flags = flags(description.flags); let (descriptor, default) = match description.typ { @@ -239,7 +242,7 @@ fn convert_description_to_ctrl_body(description: Description) -> Option ( ControlValueDescriptor::Integer(Range::new( 0, - u8::MAX_VALUE as i64, + u8::MAX as i64, Some(description.step as i64), )), Some(ControlValue::Integer(description.default)), @@ -247,7 +250,7 @@ fn convert_description_to_ctrl_body(description: Description) -> Option ( ControlValueDescriptor::Integer(Range::new( 0, - u16::MAX_VALUE as i64, + u16::MAX as i64, Some(description.step as i64), )), Some(ControlValue::Integer(description.default)), @@ -255,7 +258,7 @@ fn convert_description_to_ctrl_body(description: Description) -> Option ( ControlValueDescriptor::Integer(Range::new( 0, - u32::MAX_VALUE as i64, + u32::MAX as i64, Some(description.step as i64), )), Some(ControlValue::Integer(description.default)), @@ -271,15 +274,15 @@ fn convert_description_to_ctrl_body(description: Description) -> Option { // our keys - let descriptor = match description.items { + let descriptor = match &description.items { Some(items) => ControlValueDescriptor::Menu( items .into_iter() .map(|(idx, menu_item)| { ( - ControlValue::Integer(idx as i64), + ControlValue::Integer(*idx as i64), match menu_item { - MenuItem::Name(name) => ControlValue::String(name), + MenuItem::Name(name) => ControlValue::String(name.clone()), MenuItem::Value(v) => ControlValue::Integer(*v), }, ) @@ -306,31 +309,28 @@ fn convert_description_to_ctrl_body(description: Description) -> Option Result { - let value = match control { + let value = match &control { ControlValue::Null => Value::None, - ControlValue::Integer(i) | ControlValue::BitMask(i) => Value::Integer(i), - ControlValue::String(s) => Value::String(s), - ControlValue::Boolean(t) => Value::Boolean(t), - ControlValue::Binary(b) => Value::CompoundU8(b), + ControlValue::Integer(i) => Value::Integer(*i), + ControlValue::BitMask(bm) => Value::Integer(*bm as i64), + ControlValue::String(s) => Value::String(s.clone()), + ControlValue::Boolean(t) => Value::Boolean(*t), + ControlValue::Binary(b) => Value::CompoundU8(b.clone()), ControlValue::EnumPick(e) => { - if let ControlValue::Integer(i) = &e { + if let ControlValue::Integer(i) = &**e { Value::Integer(*i) } else { - return Err(NokhwaError::ConversionError( - "could not convert non integer enum pick".to_string(), - )); + return Err(NokhwaError::InvalidControlValue(control.clone())); } } ControlValue::Orientation(o) => Value::Integer(match o { Orientation::User => V4L2_CAMERA_ORIENTATION_FRONT as i64, Orientation::Environment => V4L2_CAMERA_ORIENTATION_BACK as i64, - Orientation::Custom(i) => i, + Orientation::Custom(i) => *i, _ => V4L2_CAMERA_ORIENTATION_EXTERNAL as i64, }), _ => { - return Err(NokhwaError::ConversionError( - "Conversion not supported for this data type.".to_string(), - )) + return Err(NokhwaError::InvalidControlValue(control.clone())); } }; @@ -351,7 +351,7 @@ impl PlatformTrait for V4L2Platform { true } - fn query(&mut self) -> NokhwaResult> { + fn query(&mut self) -> NokhwaResult> { Ok(enum_devices() .into_iter() .map(|v4l_node| { @@ -360,7 +360,10 @@ impl PlatformTrait for V4L2Platform { Device::new(index) .map(|dev| { dev.query_caps() - .map(|caps| index_capabilities_to_camera_info(index as u32, caps)) + .map(|caps| QueriedCamera { + index: CameraIndex::Index(index as u32), + information: index_capabilities_to_camera_info(caps), + }) .ok() }) .ok() @@ -374,18 +377,15 @@ impl PlatformTrait for V4L2Platform { let device = match &index { CameraIndex::Index(i) => Device::new(*i as usize), CameraIndex::String(path) => Device::with_path(path), + CameraIndex::Stable(_) => { + return Err(NokhwaError::UnsupportedOperationError( + Backends::Video4Linux2, + )) + } } - .map_err(|why| NokhwaError::OpenDeviceError(index.to_string(), why.to_string()))?; + .map_err(|why| NokhwaError::OpenDeviceError(index, why.to_string()))?; - let mut v4l2_camera = V4L2Camera { - device, - camera_format: None, - camera_index: index, - controls: Default::default(), - stream: None, - }; - - v4l2_camera.refresh_controls()?; + let v4l2_camera = V4L2Camera { device }; Ok(v4l2_camera) } @@ -393,369 +393,450 @@ impl PlatformTrait for V4L2Platform { pub struct V4L2Camera { device: Device, - camera_format: Option, - camera_index: CameraIndex, - controls: Controls, - stream: Option, } -impl Setting for V4L2Camera { +impl CameraTrait for V4L2Camera { + type Stream = V4L2Stream; + fn enumerate_formats(&self) -> Result, NokhwaError> { let mut formats = vec![]; - for frame_format in self + for (desc, frame_format) in self .device .enum_formats() - .map_err(|why| NokhwaError::GetPropertyError { - property: "enum_formats".to_string(), - error: why.to_string(), - })? + .map_err(|why| NokhwaError::ListFourCCError(why.to_string()))? .into_iter() - .map(|desc| fourcc_to_frame_format(desc.fourcc)) + .map(|desc| { + let fourcc = fourcc_to_frame_format(desc.fourcc); + (desc, fourcc) + }) { + let resolutions = self + .device + .enum_framesizes(desc.fourcc) + .map_err(|why| NokhwaError::ListResolutionError(why.to_string()))? + .into_iter() + .flat_map(|frame_size| frame_size.size.to_discrete()) + .map(|discrete| Resolution::new(discrete.width, discrete.height)) + .collect::>(); + + let v4l2_frame_intervals = resolutions + .iter() + .map(|resolution| { + let frame_intervals = self.device.enum_frameintervals( + desc.fourcc, + resolution.width(), + resolution.height(), + ); + + frame_intervals.and_then(|ints| Ok((*resolution, ints))) + }) + .collect::)>, std::io::Error>>() + .map_err(|why| NokhwaError::ListFrameRatesError(why.to_string()))?; + formats.extend( - self.enumerate_resolution_and_frame_rates(frame_format)? - .into_iter() - .flat_map(|(resolution, frame_rates)| { - frame_rates.into_iter().map(|frame_rate| { - CameraFormat::new(resolution, frame_format, frame_rate) - }) + v4l2_frame_intervals + .iter() + .flat_map(|(resolution, interval)| { + interval + .iter() + .flat_map(|frame_interval| match &frame_interval.interval { + FrameIntervalEnum::Discrete(fraction) => { + vec![FrameRate::new(fraction.numerator, fraction.denominator)] + } + FrameIntervalEnum::Stepwise(stepwise) => { + let min = stepwise.min.numerator; + let max = stepwise.max.numerator; + + // short circuit if denominators differ + if stepwise.step.denominator != stepwise.max.denominator + || stepwise.step.denominator != stepwise.min.denominator + { + return vec![]; + } + + (min..max) + .step_by(stepwise.step.numerator as usize) + .map(|num| FrameRate::new(num, stepwise.step.denominator)) + .collect() + } + }) + .map(|frame_rate| { + CameraFormat::new(*resolution, frame_format, frame_rate) + }) }), ); } + formats.dedup(); Ok(formats) } - fn enumerate_resolution_and_frame_rates( - &self, - frame_format: FrameFormat, - ) -> Result>, NokhwaError> { - let fourcc = frame_format_to_fourcc(frame_format)?; - let resolutions = self - .device - .enum_framesizes(fourcc) - .map_err(|why| NokhwaError::GetPropertyError { - property: "enum_framesizes".to_string(), - error: why.to_string(), - })? - .into_iter() - .flat_map(|frame_size| frame_size.size.to_discrete()) - .map(|discrete| Resolution::new(discrete.width, discrete.height)) - .collect::>(); - - let v4l2_frame_intervals = resolutions - .iter() - .map(|resolution| { - ( - *resolution, - self.device.enum_frameintervals( - fourcc, - resolution.width(), - resolution.height(), - ), - ) - }) - .collect::)>, std::io::Error>>() - .map_err(|why| NokhwaError::GetPropertyError { - property: "enum_frameintervals".to_string(), - error: why.to_string(), - })?; - - Ok(v4l2_frame_intervals - .into_iter() - .flatten() - .flat_map(|(resolution, interval)| { - match interval.interval { - FrameIntervalEnum::Discrete(discrete) => { - NonZeroI32::new(discrete.denominator as i32).map(|denominator| { - ( - resolution, - vec![FrameRate::new(discrete.numerator as i32, denominator)], - ) - }) - } - FrameIntervalEnum::Stepwise(stepwise) => { - // we have to do this ourselves - - // no logic to handle different or zero demoninator - if (stepwise.step.denominator != stepwise.max.denominator) - || (stepwise.step.denominator != stepwise.min.denominator) - { - return None; - } - - let min = stepwise.min.numerator as i32; - let max = stepwise.max.numerator as i32; - let step = stepwise.step.numerator as i32; - let denominator = stepwise.step.denominator as i32; - - NonZeroI32::new(denominator).map(|denominator| { - ( - resolution, - (min..max) - .step_by(step as usize) - .map(|numerator| FrameRate::new(numerator, denominator)) - .collect::>(), - ) - }) - } - } - }) - .flatten() - .collect::>>()) - } - - fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError> { - let fourcc = frame_format_to_fourcc(*camera_format.format())?; - self.device - .set_format(&Format::new( - camera_format.width(), - camera_format.height(), - fourcc, - )) - .map_err(|why| NokhwaError::SetPropertyError { - property: "set_format".to_string(), - value: format!("format: {camera_format} fourcc: {fourcc}"), - error: why.to_string(), - })?; - self.device - .set_params(&Parameters::new(Fraction::new( - *camera_format.frame_rate().numerator() as u32, - *camera_format.frame_rate().denominator() as u32, - ))) - .map_err(|why| NokhwaError::SetPropertyError { - property: "set_params".to_string(), - value: format!("{}", camera_format.frame_rate()), - error: why.to_string(), - })?; - self.camera_format = Some(camera_format); - Ok(()) - } - - fn control_ids(&self) -> Keys { - self.controls.ids() - } - - fn control_descriptions(&self) -> Values { - self.controls.descriptions() - } - - fn control_values(&self) -> Values { - self.controls.values() - } - - fn control_value(&self, id: &ControlId) -> Option<&ControlValue> { - self.controls.value(id) - } - - fn control_description(&self, id: &ControlId) -> Option<&ControlDescription> { - self.controls.description(id) - } - - fn set_control( - &mut self, - property: &ControlId, - value: ControlValue, - ) -> Result<(), NokhwaError> { - if !self.controls.validate(property, &value)? { - return Err(NokhwaError::SetPropertyError { - property: property.to_string(), - value: value.to_string(), - error: "failed to validate".to_string(), - }); - } - let cid = control_id_to_cid(*property)?; - let v4l_value = conv_control_value_to_v4l_value(value.clone())?; - self.device - .set_control(Control { - id: cid, - value: v4l_value, - }) - .map_err(|why| { - Err(NokhwaError::SetPropertyError { - property: cid.to_string(), - value: value.to_string(), - error: why.to_string(), - }) - })?; - self.controls.set_control_value(property, value)?; - Ok(()) - } - - fn refresh_controls(&mut self) -> Result<(), NokhwaError> { - let descriptions = self + fn controls(&self) -> Result, NokhwaError> { + let controls = self .device .query_controls() - .map_err(|why| NokhwaError::GetPropertyError { - property: "query_controls".to_string(), - error: why.to_string(), - })? - .into_iter() - .map(|description| { - let id = cid_to_control_id(description.id); - - convert_description_to_ctrl_body(description).map(|body| (id, body)) - }) - .flatten() - .collect::>(); - - let values = descriptions - .keys() - .into_iter() - .copied() - .flat_map(|k| control_id_to_cid(k).map(|cid| (k, cid))) - .flat_map(|(id, cid)| self.device.control(cid).map(|v| (id, v))) - .map(|(id, value)| { - ( - id, - match value.value { - Value::None => ControlValue::Null, - Value::Integer(i) => ControlValue::Integer(i), - Value::Boolean(b) => ControlValue::Boolean(b), - Value::String(s) => ControlValue::String(s), - Value::CompoundU8(bin) | Value::CompoundPtr(bin) => { - ControlValue::Binary(bin) - } - Value::CompoundU16(u) | Value::CompoundU32(u) => ControlValue::Array( - u.into_iter() - .map(|u| ControlValue::Integer(u as i64)) - .collect(), - ), - }, - ) - }) - .collect::>(); - - match Controls::new(descriptions, values) { - Some(c) => { - self.controls = c; - } - None => { - return Err(NokhwaError::SetPropertyError { - property: "control".to_string(), - value: format!("{:?} {:?}", descriptions, values), - error: "Failed to convert to control".to_string(), + .map_err(|why| NokhwaError::ListControlError(why.to_string()))? + .iter() + .filter_map(|descriptor| { + convert_description_to_ctrl_body(descriptor).map(|x| NokhwaControl { + id: cid_to_control_id(descriptor.id), + description: x, }) - } - } - - Ok(()) + }) + .collect::>(); + Ok(controls) } -} -struct V4L2Stream { - thread: JoinHandle<()>, - control: Arc>, -} - -impl Drop for V4L2Stream { - fn drop(&mut self) { - let _ = self.control.send(()); + fn control_value(&self, id: ControlId) -> Result { + todo!() } -} -impl Capture for V4L2Camera { - fn open_stream( + fn set_control(&self, id: ControlId, value: ControlValue) -> Result<(), NokhwaError> { + todo!() + } + + fn open_stream( &mut self, - stream_configuration: Option, - ) -> Result { - if let Some(_) = self.stream { - return Err(NokhwaError::OpenStreamError( - "StreamAlreadyOpen".to_string(), - )); - } - - let stream_config = stream_configuration.unwrap_or_default(); - - let format = match self.camera_format { - Some(fmt) => fmt, - None => return Err(NokhwaError::OpenStreamError("No Format".to_string())), - }; - - let (control, ctrl_recv) = bounded(1); - let (sender, receiver) = match stream_config.bound { - StreamBounds::Bounded(b) => bounded(b as usize), - StreamBounds::Unbounded => unbounded(), - }; - - let control = Arc::new(control); - - let mut mmap_stream = MmapStream::new(&self.device, v4l::buffer::Type::VideoCapture) - .map_err(|why| return NokhwaError::OpenStreamError(why.to_string()))?; - - let stream_handle = StreamHandle::new(receiver, control.clone(), stream_config, format); - - let thread = std::thread::spawn(move || loop { - if ctrl_recv.is_disconnected() || sender.is_disconnected() { - return; - } - if let Ok(_) = ctrl_recv.try_recv() { - let _ = sender.send(Event::Closed); - return; - } - - match mmap_stream.next() { - Ok((data, meta)) => { - let data = Cow::Owned(data.to_owned()); - let mut metadata = Metadata::new(); - - metadata.insert( - CompactString::from("flags"), - ControlValue::BitMask(meta.flags.bits() as u64), - ); - metadata.insert( - CompactString::from("time_secs"), - ControlValue::Integer(meta.timestamp.sec), - ); - metadata.insert( - CompactString::from("time_usecs"), - ControlValue::Integer(meta.timestamp.usec), - ); - metadata.insert( - CompactString::from("size"), - ControlValue::Integer(meta.bytesused as i64), - ); - metadata.insert( - CompactString::from("sequence"), - ControlValue::Integer(meta.sequence as i64), - ); - metadata.insert( - CompactString::from("field"), - ControlValue::Integer(meta.field as i64), - ); - - let _ = sender.send(Event::NewFrame(FrameBuffer::new(data, Some(metadata)))); - } - Err(why) => { - let _ = sender.send(Event::Error(Box::new(why))); - } - } - }); - - self.stream = Some(V4L2Stream { thread, control }); - - Ok(stream_handle) + camera_format: CameraFormat, + frame_callback: FrameCallback, + error_callback: ErrorCallback, + ) -> Result + where + FrameCallback: FnMut(nokhwa_core::frame_buffer::FrameBuffer<'_>) + Send + 'static, + ErrorCallback: FnMut(NokhwaError) + Send + 'static, + { + todo!() } +} - fn close_stream(&mut self) -> Result<(), NokhwaError> { - let mut stream = match std::mem::take(&mut self.stream) { - Some(s) => s, - None => { - return Err(NokhwaError::StreamShutdownError( - "No stream to shutdown".to_string(), - )) - } - }; +pub struct V4L2Stream { + format: CameraFormat, + join_handle: JoinHandle<()>, + stop: AtomicBool, - let _ = stream.control.send(()); - - stream - .thread - .join() - .map_err(|why| NokhwaError::StreamShutdownError(format!("{why:?}")))?; +} +impl V4L2Stream { + fn new( + mut stream: MmapStream<'_>, + format: CameraFormat, + index: CameraIndex, + fcb: FrameCallback, + ecb: ErrorCallback, + ) -> Result + where + FrameCallback: FnMut(nokhwa_core::frame_buffer::FrameBuffer<'_>) + Send + 'static, + ErrorCallback: FnMut(NokhwaError) + Send + 'static, + { Ok(()) } } -impl Camera for V4L2Camera {} +impl StreamTrait for V4L2Stream { + fn current_format(&self) -> &CameraFormat { + &self.format + } + + fn stop_stream(mut self) -> Result<(), NokhwaError> { + self.device.stop()?; + Ok(()) + } +} + +fn frame_poller_v4l2( + index: CameraIndex, + mut stream: MmapStream<'_>, + mut fcb: FrameCallback, + mut ecb: ErrorCallback, +) where + FrameCallback: FnMut(nokhwa_core::frame_buffer::FrameBuffer<'_>) + Send + 'static, + ErrorCallback: FnMut(NokhwaError) + Send + 'static, +{ + loop { + let (frame_data, metadata) = match stream.next() { + Ok((f, m)) => (f, m), + Err(why) => { + // match why { + // Ok(_) => todo!(), + // Err(_) => todo!(), + // } + ecb(NokhwaError::StreamError(StreamError::StreamInvalidated)); + break; + }, + }; + let converted_metadata = { + let mut meta = Metadata::new(); + meta.insert(MetadataTypes::Size(metadata.bytesused as u64)); + let time = Time:: + meta.insert(MetadataTypes::Timestamp()); + meta.insert(MetadataTypes::Size(metadata.bytesused as u64)); + } + } +} + +// pub struct V4L2Camera<'a> { +// device: Device, +// camera_format: Option, +// camera_index: CameraIndex, +// controls: Controls, +// stream: Option>, +// _phantom: PhantomData<&'a V4L2Platform>, +// } + +// impl<'a> Setting for V4L2Camera<'a> { +// fn enumerate_formats(&self) -> Result, NokhwaError> { +// let mut formats = vec![]; + +// for frame_format in self +// .device +// .enum_formats() +// .map_err(|why| NokhwaError::GetPropertyError { +// property: "enum_formats".to_string(), +// error: why.to_string(), +// })? +// .into_iter() +// .map(|desc| fourcc_to_frame_format(desc.fourcc)) +// { +// formats.extend( +// self.enumerate_resolution_and_frame_rates(frame_format)? +// .into_iter() +// .flat_map(|(resolution, frame_rates)| { +// frame_rates.into_iter().map(move |frame_rate| { +// CameraFormat::new(resolution, frame_format, frame_rate) +// }) +// }), +// ); +// } +// Ok(formats) +// } + +// fn enumerate_resolution_and_frame_rates( +// &self, +// frame_format: FrameFormat, +// ) -> Result>, NokhwaError> { +// let fourcc = frame_format_to_fourcc(frame_format)?; +// let resolutions = self +// .device +// .enum_framesizes(fourcc) +// .map_err(|why| NokhwaError::GetPropertyError { +// property: "enum_framesizes".to_string(), +// error: why.to_string(), +// })? +// .into_iter() +// .flat_map(|frame_size| frame_size.size.to_discrete()) +// .map(|discrete| Resolution::new(discrete.width, discrete.height)) +// .collect::>(); + +// let v4l2_frame_intervals = resolutions +// .iter() +// .map(|resolution| { +// let frame_intervals = self.device.enum_frameintervals( +// fourcc, +// resolution.width(), +// resolution.height(), +// ); + +// frame_intervals.and_then(|ints| Ok((*resolution, ints))) +// }) +// .collect::)>, std::io::Error>>() +// .map_err(|why| NokhwaError::GetPropertyError { +// property: "enum_frameintervals".to_string(), +// error: why.to_string(), +// })?; + +// Ok(v4l2_frame_intervals +// .iter() +// .flat_map(|(resolution, interval)| { +// interval.iter().map(|int| { +// match &int.interval { +// FrameIntervalEnum::Discrete(discrete) => { +// NonZeroI32::new(discrete.denominator as i32).map(|denominator| { +// ( +// *resolution, +// vec![FrameRate::new(discrete.numerator as i32, denominator)], +// ) +// }) +// } +// FrameIntervalEnum::Stepwise(stepwise) => { +// // we have to do this ourselves + +// // no logic to handle different or zero demoninator +// if (stepwise.step.denominator != stepwise.max.denominator) +// || (stepwise.step.denominator != stepwise.min.denominator) +// { +// return None; +// } + +// let min = stepwise.min.numerator as i32; +// let max = stepwise.max.numerator as i32; +// let step = stepwise.step.numerator as i32; +// let denominator = stepwise.step.denominator as i32; + +// NonZeroI32::new(denominator).map(|denominator| { +// ( +// *resolution, +// (min..max) +// .step_by(step as usize) +// .map(|numerator| FrameRate::new(numerator, denominator)) +// .collect::>(), +// ) +// }) +// } +// } +// }) +// }) +// .flatten() +// .collect::>>()) +// } + +// fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError> { +// let fourcc = frame_format_to_fourcc(camera_format.format())?; +// self.device +// .set_format(&Format::new( +// camera_format.width(), +// camera_format.height(), +// fourcc, +// )) +// .map_err(|why| NokhwaError::SetPropertyError { +// property: "set_format".to_string(), +// value: format!("format: {camera_format} fourcc: {fourcc}"), +// error: why.to_string(), +// })?; +// self.device +// .set_params(&Parameters::new(Fraction::new( +// camera_format.frame_rate().numerator() as u32, +// camera_format.frame_rate().denominator() as u32, +// ))) +// .map_err(|why| NokhwaError::SetPropertyError { +// property: "set_params".to_string(), +// value: format!("{}", camera_format.frame_rate()), +// error: why.to_string(), +// })?; +// self.camera_format = Some(camera_format); +// Ok(()) +// } + +// fn control_ids(&self) -> Keys { +// self.controls.ids() +// } + +// fn control_descriptions(&self) -> Values { +// self.controls.descriptions() +// } + +// fn control_values(&self) -> Values { +// self.controls.values() +// } + +// fn control_value(&self, id: &ControlId) -> Option<&ControlValue> { +// self.controls.value(id) +// } + +// fn control_description(&self, id: &ControlId) -> Option<&ControlDescription> { +// self.controls.description(id) +// } + +// fn set_control( +// &mut self, +// property: &ControlId, +// value: ControlValue, +// ) -> Result<(), NokhwaError> { +// if !self.controls.validate(property, &value)? { +// return Err(NokhwaError::SetPropertyError { +// property: property.to_string(), +// value: value.to_string(), +// error: "failed to validate".to_string(), +// }); +// } +// let cid = control_id_to_cid(*property)?; +// let v4l_value = conv_control_value_to_v4l_value(value.clone())?; +// self.device +// .set_control(Control { +// id: cid, +// value: v4l_value, +// }) +// .map_err(|why| NokhwaError::SetPropertyError { +// property: cid.to_string(), +// value: value.to_string(), +// error: why.to_string(), +// })?; +// self.controls.set_control_value(property, value)?; +// Ok(()) +// } + +// fn refresh_controls(&mut self) -> Result<(), NokhwaError> { +// let descriptions = self +// .device +// .query_controls() +// .map_err(|why| NokhwaError::GetPropertyError { +// property: "query_controls".to_string(), +// error: why.to_string(), +// })? +// .into_iter() +// .map(|description| { +// let id = cid_to_control_id(description.id); + +// convert_description_to_ctrl_body(description).map(|body| (id, body)) +// }) +// .flatten() +// .collect::>(); + +// let values = descriptions +// .keys() +// .into_iter() +// .copied() +// .flat_map(|k| control_id_to_cid(k).map(|cid| (k, cid))) +// .flat_map(|(id, cid)| self.device.control(cid).map(|v| (id, v))) +// .map(|(id, value)| { +// ( +// id, +// match value.value { +// Value::None => ControlValue::Null, +// Value::Integer(i) => ControlValue::Integer(i), +// Value::Boolean(b) => ControlValue::Boolean(b), +// Value::String(s) => ControlValue::String(s), +// Value::CompoundU8(bin) | Value::CompoundPtr(bin) => { +// ControlValue::Binary(bin) +// } +// Value::CompoundU16(c_16) => ControlValue::Array( +// c_16.into_iter() +// .map(|u| ControlValue::Integer(u as i64)) +// .collect(), +// ), +// Value::CompoundU32(c_32) => ControlValue::Array( +// c_32.into_iter() +// .map(|u| ControlValue::Integer(u as i64)) +// .collect(), +// ), +// }, +// ) +// }) +// .collect::>(); + +// match Controls::new(descriptions, values) { +// Some(c) => { +// self.controls = c; +// } +// None => { +// return Err(NokhwaError::SetPropertyError { +// property: "control".to_string(), +// value: format!(""), +// error: "Failed to convert to control".to_string(), +// }) +// } +// } + +// Ok(()) +// } +// } + +// struct V4L2Stream<'a> { +// thread: ScopedJoinHandle<'a, ()>, +// control: Arc>, +// } + +// impl<'a> V4L2Stream<'a> { +// pub fn join(self) -> Result<(), ()> { +// self.thread.join().map_err(|_| ()) +// } +// } diff --git a/nokhwa-bindings-linux/src/v4l2r.rs b/nokhwa-bindings-linux/src/v4l2r.rs deleted file mode 100644 index 70b786d..0000000 --- a/nokhwa-bindings-linux/src/v4l2r.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index b7e6fec..ea65a16 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -1245,110 +1245,143 @@ pub mod wmf { #[allow(clippy::needless_pass_by_value)] #[allow(clippy::must_use_candidate)] pub mod wmf { - use nokhwa_core::control::{CameraControl, ControlValue, KnownCameraControl}; - use nokhwa_core::error::NokhwaError; - use nokhwa_core::types::{CameraFormat, CameraIndex, CameraInformation}; - use std::borrow::Cow; + use nokhwa_core::{ + camera::{Camera, Capture, Setting}, + platform::PlatformTrait, + types::Backends, + }; - pub fn initialize_mf() -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Not on windows".to_string(), - )) - } + pub struct MSMFCamera {} - pub fn de_initialize_mf() -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Not on windows".to_string(), - )) - } - - pub fn query_msmf() -> Result, NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Not on windows".to_string(), - )) - } - - pub struct MediaFoundationDevice { - camera: CameraIndex, - } - - impl MediaFoundationDevice { - pub fn new(_index: CameraIndex) -> Result { - Ok(MediaFoundationDevice { - camera: CameraIndex::Index(0), - }) + impl Setting for MSMFCamera { + fn enumerate_formats( + &self, + ) -> Result, nokhwa_core::error::NokhwaError> + { + todo!() } - pub fn index(&self) -> &CameraIndex { - &self.camera + fn enumerate_resolution_and_frame_rates( + &self, + frame_format: nokhwa_core::frame_format::FrameFormat, + ) -> Result< + std::collections::HashMap< + nokhwa_core::types::Resolution, + Vec, + >, + nokhwa_core::error::NokhwaError, + > { + todo!() } - pub fn name(&self) -> String { - String::new() - } - - pub fn symlink(&self) -> String { - String::new() - } - - pub fn compatible_format_list(&mut self) -> Result, NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) - } - - pub fn control(&self, _control: KnownCameraControl) -> Result { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) - } - - pub fn set_control( + fn set_format( &mut self, - _control: KnownCameraControl, - _value: ControlValue, - ) -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) + camera_format: nokhwa_core::types::CameraFormat, + ) -> Result<(), nokhwa_core::error::NokhwaError> { + todo!() } - pub fn format_refreshed(&mut self) -> Result { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) + fn control_ids( + &self, + ) -> std::collections::hash_map::Keys< + '_, + nokhwa_core::control::ControlId, + nokhwa_core::control::ControlDescription, + > { + todo!() } - pub fn format(&self) -> CameraFormat { - CameraFormat::default() + fn control_descriptions( + &self, + ) -> std::collections::hash_map::Values< + '_, + nokhwa_core::control::ControlId, + nokhwa_core::control::ControlDescription, + > { + todo!() } - pub fn set_format(&mut self, _format: CameraFormat) -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) + fn control_values( + &self, + ) -> std::collections::hash_map::Values< + '_, + nokhwa_core::control::ControlId, + nokhwa_core::control::ControlValue, + > { + todo!() } - pub fn is_stream_open(&self) -> bool { - false + fn control_value( + &self, + id: &nokhwa_core::control::ControlId, + ) -> Option<&nokhwa_core::control::ControlValue> { + todo!() } - pub fn start_stream(&mut self) -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) + fn control_description( + &self, + id: &nokhwa_core::control::ControlId, + ) -> Option<&nokhwa_core::control::ControlDescription> { + todo!() } - pub fn raw_bytes(&mut self) -> Result, NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Only on Windows".to_string(), - )) + fn set_control( + &mut self, + property: &nokhwa_core::control::ControlId, + value: nokhwa_core::control::ControlValue, + ) -> Result<(), nokhwa_core::error::NokhwaError> { + todo!() } - pub fn stop_stream(&mut self) {} + fn refresh_controls(&mut self) -> Result<(), nokhwa_core::error::NokhwaError> { + todo!() + } } - impl Drop for MediaFoundationDevice { - fn drop(&mut self) {} + impl Capture for MSMFCamera { + fn open_stream( + &mut self, + stream_configuration: Option, + ) -> Result< + std::sync::Arc>, + nokhwa_core::error::NokhwaError, + > { + todo!() + } + + fn close_stream(&mut self) -> Result<(), nokhwa_core::error::NokhwaError> { + todo!() + } + } + + impl Camera for MSMFCamera {} + + pub struct MSMFPlatform {} + + impl PlatformTrait for MSMFPlatform { + const PLATFORM: nokhwa_core::types::Backends = Backends::MicrosoftMediaFoundation; + + type Camera = MSMFCamera; + + fn block_on_permission(&mut self) -> nokhwa_core::error::NokhwaResult<()> { + todo!() + } + + fn check_permission_given(&mut self) -> bool { + todo!() + } + + fn query( + &mut self, + ) -> nokhwa_core::error::NokhwaResult> { + todo!() + } + + fn open( + &mut self, + index: nokhwa_core::types::CameraIndex, + ) -> nokhwa_core::error::NokhwaResult { + todo!() + } } } diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index af5f503..e91252c 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/l1npengtul/nokhwa" [features] default = [] -serialize = ["serde", "ordered-float/serde"] +serialize = ["serde", "ordered-float/serde", "uuid/serde"] wgpu = ["wgpu-types"] opencv-mat = ["opencv", "opencv/clang-runtime"] docs-features = ["serialize", "wgpu-types"] @@ -22,14 +22,17 @@ test-fail-warnings = [] [dependencies] thiserror = "2.0" -flume = "0.11" +flume = "0.12" num-traits = "0.2" ordered-float = "5" -typed-builder = "0.21" -compact_str = "0.9" +typed-builder = "0.23" bytemuck = "1.23" -smallmap = "1.4" paste = "1.0" +uuid = "1.19" + +[dependencies.time] +version = "0.3" +default-features = false [dependencies.num-rational] version = "0.4" diff --git a/nokhwa-core/src/camera.rs b/nokhwa-core/src/camera.rs index 7602198..10daacd 100644 --- a/nokhwa-core/src/camera.rs +++ b/nokhwa-core/src/camera.rs @@ -1,94 +1,70 @@ -use crate::control::{ControlDescription, ControlId, ControlValue}; +use crate::control::{Control, ControlId, ControlValue}; use crate::error::NokhwaError; -use crate::frame_format::FrameFormat; -use crate::stream::{StreamConfiguration, StreamHandle}; -use crate::types::{CameraFormat, FrameRate, Resolution}; -use std::collections::HashMap; -use std::collections::hash_map::{Keys, Values}; -use std::sync::Arc; +use crate::frame_buffer::FrameBuffer; +use crate::stream::StreamTrait; +use crate::types::CameraFormat; + +pub trait CameraTrait { + type Stream: StreamTrait; -pub trait Setting { - /// # Errors - /// Will error on fn enumerate_formats(&self) -> Result, NokhwaError>; - /// # Errors - /// Will error on - fn enumerate_resolution_and_frame_rates( - &self, - frame_format: FrameFormat, - ) -> Result>, NokhwaError>; + fn controls(&self) -> Result, NokhwaError>; - /// # Errors - /// Will error on - fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>; + fn control_value(&self, id: ControlId) -> Result; - fn control_ids(&self) -> Keys<'_, ControlId, ControlDescription>; + fn set_control(&self, id: ControlId, value: ControlValue) -> Result<(), NokhwaError>; - fn control_descriptions(&self) -> Values<'_, ControlId, ControlDescription>; + // fn open_stream( + // &mut self, + // camera_format: CameraFormat, + // frame_callback: FrameCallback, + // error_callback: ErrorCallback, + // ) -> Result + // where + // FrameCallback: FnMut(FrameBuffer<'_>) + Send + 'static, + // ErrorCallback: FnMut(StreamEvent) + Send + 'static; - fn control_values(&self) -> Values<'_, ControlId, ControlValue>; - - fn control_value(&self, id: &ControlId) -> Option<&ControlValue>; - - fn control_description(&self, id: &ControlId) -> Option<&ControlDescription>; - - /// # Errors - /// Will error on - fn set_control(&mut self, property: &ControlId, value: ControlValue) - -> Result<(), NokhwaError>; - - /// # Errors - /// Will error on - fn refresh_controls(&mut self) -> Result<(), NokhwaError>; -} - -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncSetting { - async fn enumerate_formats_async(&self) -> Result, NokhwaError>; - - async fn enumerate_resolution_and_frame_rates_async( - &self, - frame_format: FrameFormat, - ) -> Result>, NokhwaError>; - - async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - - async fn set_property_async( + fn open_stream( &mut self, - property: &ControlId, - value: ControlValue, - ) -> Result<(), NokhwaError>; + camera_format: CameraFormat, + frame_callback: FrameCallback, + error_callback: ErrorCallback, + ) -> Result + where + FrameCallback: FnMut(FrameBuffer<'_>) + Send + 'static, + ErrorCallback: FnMut(NokhwaError) + Send + 'static; } -pub trait Capture { - /// Implementations MUST guarantee that there can only ever be one stream open at once. - /// # Errors - /// Errors are driver specific - fn open_stream( - &mut self, - stream_configuration: Option, - ) -> Result>, NokhwaError>; +// #[cfg(feature = "async")] +// #[cfg_attr(feature = "async", async_trait::async_trait)] +// pub trait AsyncSetting { +// async fn enumerate_formats_async(&self) -> Result, NokhwaError>; - // Implementations MUST be multi-close tolerant. - /// # Errors - /// Errors are driver specific - fn close_stream(&mut self) -> Result<(), NokhwaError>; -} +// async fn enumerate_resolution_and_frame_rates_async( +// &self, +// frame_format: FrameFormat, +// ) -> Result>, NokhwaError>; -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncStream { - async fn open_stream_async<'a>( - &mut self, - stream_configuration: Option, - ) -> Result, NokhwaError>; +// async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - async fn close_stream_async(&mut self) -> Result<(), NokhwaError>; -} +// async fn set_property_async( +// &mut self, +// property: &ControlId, +// value: ControlValue, +// ) -> Result<(), NokhwaError>; +// } -pub trait Camera: Setting + Capture {} +// #[cfg(feature = "async")] +// #[cfg_attr(feature = "async", async_trait::async_trait)] +// pub trait AsyncStream { +// async fn open_stream_async<'a>( +// &mut self, +// stream_configuration: Option, +// ) -> Result, NokhwaError>; -#[cfg(feature = "async")] -pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {} +// async fn close_stream_async(&mut self) -> Result<(), NokhwaError>; +// } + +// // #[cfg(feature = "async")] +// // pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {} diff --git a/nokhwa-core/src/control.rs b/nokhwa-core/src/control.rs index a89e095..09ff458 100644 --- a/nokhwa-core/src/control.rs +++ b/nokhwa-core/src/control.rs @@ -1,13 +1,34 @@ -use crate::error::{NokhwaError, NokhwaResult}; use crate::ranges::{Range, ValidatableRange}; -use compact_str::CompactString; use ordered_float::OrderedFloat; -use std::collections::hash_map::{Keys, Values}; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; use std::hash::Hash; +pub use uuid::Uuid; -pub type PlatformSpecificControlId = u64; +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub enum CustomControlId { + U32(u32), + U64(u64), + Uuid(Uuid), +} + +impl From for CustomControlId { + fn from(value: u32) -> Self { + CustomControlId::U32(value) + } +} + +impl From for CustomControlId { + fn from(value: u64) -> Self { + CustomControlId::U64(value) + } +} + +impl From for CustomControlId { + fn from(value: Uuid) -> Self { + CustomControlId::Uuid(value) + } +} #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] pub enum ControlId { @@ -44,7 +65,7 @@ pub enum ControlId { Orientation, - PlatformSpecific(PlatformSpecificControlId), + Custom(CustomControlId), } impl Display for ControlId { @@ -53,115 +74,115 @@ impl Display for ControlId { } } -#[derive(Clone, Debug, Default, PartialEq)] -pub struct Controls { - descriptions: HashMap, - values: HashMap, -} +// #[derive(Clone, Debug, Default, PartialEq)] +// pub struct Controls { +// descriptions: HashMap, +// values: HashMap, +// } -impl Controls { - /// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls` - #[must_use] - pub fn new( - device_controls: HashMap, - device_values: HashMap, - ) -> Option { - for (id, value) in &device_values { - if let Some(description) = device_controls.get(id) - && !description.validate(value) - { - return None; - } - } +// impl Controls { +// /// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls` +// #[must_use] +// pub fn new( +// device_controls: HashMap, +// device_values: HashMap, +// ) -> Option { +// for (id, value) in &device_values { +// if let Some(description) = device_controls.get(id) +// && !description.validate(value) +// { +// return None; +// } +// } - Some(Self { - descriptions: device_controls, - values: device_values, - }) - } +// Some(Self { +// descriptions: device_controls, +// values: device_values, +// }) +// } - #[must_use] - pub fn empty() -> Self { - Self::default() - } +// #[must_use] +// pub fn empty() -> Self { +// Self::default() +// } - #[must_use] - pub fn unchecked_new( - device_controls: HashMap, - device_values: HashMap, - ) -> Self { - Self { - descriptions: device_controls, - values: device_values, - } - } +// #[must_use] +// pub fn unchecked_new( +// device_controls: HashMap, +// device_values: HashMap, +// ) -> Self { +// Self { +// descriptions: device_controls, +// values: device_values, +// } +// } - #[must_use] - pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> { - self.descriptions.get(control_id) - } +// #[must_use] +// pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> { +// self.descriptions.get(control_id) +// } - #[must_use] - pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> { - self.values.get(control_id) - } +// #[must_use] +// pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> { +// self.values.get(control_id) +// } - #[must_use] - pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> { - self.descriptions.values() - } +// #[must_use] +// pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> { +// self.descriptions.values() +// } - #[must_use] - pub fn values(&self) -> Values<'_, ControlId, ControlValue> { - self.values.values() - } +// #[must_use] +// pub fn values(&self) -> Values<'_, ControlId, ControlValue> { +// self.values.values() +// } - #[must_use] - pub fn ids(&self) -> Keys<'_, ControlId, ControlDescription> { - self.descriptions.keys() - } +// #[must_use] +// pub fn ids(&self) -> Keys<'_, ControlId, ControlDescription> { +// self.descriptions.keys() +// } - pub fn validate( - &self, - control_id: &ControlId, - value: &ControlValue, - ) -> Result { - let Some(description) = self.descriptions.get(control_id) else { - return Err(NokhwaError::GetPropertyError { - property: control_id.to_string(), - error: "ID Not Found".to_string(), - }); - }; +// pub fn validate( +// &self, +// control_id: &ControlId, +// value: &ControlValue, +// ) -> Result { +// let Some(description) = self.descriptions.get(control_id) else { +// return Err(NokhwaError::GetPropertyError { +// property: control_id.to_string(), +// error: "ID Not Found".to_string(), +// }); +// }; - if !self.values.contains_key(control_id) { - return Err(NokhwaError::GetPropertyError { - property: control_id.to_string(), - error: "ID Not Found".to_string(), - }); - } +// if !self.values.contains_key(control_id) { +// return Err(NokhwaError::GetPropertyError { +// property: control_id.to_string(), +// error: "ID Not Found".to_string(), +// }); +// } - Ok(description.validate(value)) - } +// Ok(description.validate(value)) +// } - pub fn set_control_value( - &mut self, - control_id: &ControlId, - value: ControlValue, - ) -> NokhwaResult<()> { - match self.values.get_mut(control_id) { - Some(old) => { - *old = value; - Ok(()) - } - // this should not happen, - None => Err(NokhwaError::SetPropertyError { - property: control_id.to_string(), - value: value.to_string(), - error: "ID Not Found".to_string(), - }), - } - } -} +// pub fn set_control_value( +// &mut self, +// control_id: &ControlId, +// value: ControlValue, +// ) -> NokhwaResult<()> { +// match self.values.get_mut(control_id) { +// Some(old) => { +// *old = value; +// Ok(()) +// } +// // this should not happen, +// None => Err(NokhwaError::SetPropertyError { +// property: control_id.to_string(), +// value: value.to_string(), +// error: "ID Not Found".to_string(), +// }), +// } +// } +// } #[derive(Clone, Debug, PartialEq)] pub struct ControlDescription { @@ -347,7 +368,7 @@ pub enum ControlValue { Integer(i64), BitMask(u64), Float(OrderedFloat), - String(CompactString), + String(String), Boolean(bool), Array(Vec), Binary(Vec), @@ -480,3 +501,9 @@ impl Display for Orientation { write!(f, "Orientation {self:?}") } } + +#[derive(Clone, Debug, PartialEq)] +pub struct Control { + pub id: ControlId, + pub description: ControlDescription, +} diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index 1fa35b1..64e3410 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -1,3 +1,4 @@ +use crate::control::{ControlId, ControlValue}; /* * Copyright 2022 l1npengtul / The Nokhwa Contributors * @@ -15,8 +16,9 @@ */ use crate::frame_format::{CustomFrameFormat, FrameFormat}; use crate::pixel_destination::PixelDestination; -use crate::types::Backends; -use std::fmt::Debug; +use crate::types::{Backends, CameraIndex}; +use std::fmt::{Debug, Display}; +use std::num::ParseIntError; use thiserror::Error; pub type NokhwaResult = Result; @@ -25,44 +27,59 @@ pub type NokhwaResult = Result; #[allow(clippy::module_name_repetitions)] #[derive(Error, Debug, Clone)] pub enum NokhwaError { + // NokhwaCore Errors + #[error("Failed to parse string index to u32: {0}")] + IndexParsingFailed(ParseIntError), + + // Platform Errors #[error("Could not initialize {backend}: {error}")] InitializeError { backend: Backends, error: String }, - #[error("Could not shutdown {backend}: {error}")] - ShutdownError { backend: Backends, error: String }, - #[error("Error: {0}")] - GeneralError(String), - #[error("Could not generate required structure {structure}: {error}")] - StructureError { structure: String, error: String }, #[error("Could not open device {0}: {1}")] - OpenDeviceError(String, String), - #[error("Could not get device property {property}: {error}")] - GetPropertyError { property: String, error: String }, - #[error("Could not set device property {property} with value {value}: {error}")] - SetPropertyError { - property: String, - value: String, + OpenDeviceError(CameraIndex, String), + #[error("Failed to query for cameras: {0}")] + QueryError(String), + + // Camera Errors + #[error("Failed to list FrameFormats: {0}")] + ListFourCCError(String), + #[error("Failed to list Resolutions: {0}")] + ListResolutionError(String), + #[error("Failed to list FrameRates: {0}")] + ListFrameRatesError(String), + #[error("Failed to list Controls: {0}")] + ListControlError(String), + #[error("{0:?} is an invalid control id: {1}")] + InvalidControlId(ControlId, String), + #[error("{0:?} is an invalid control value.")] + InvalidControlValue(ControlValue), + #[error("{0:?} is an invalid frame format: {1}")] + InvalidFrameFormat(FrameFormat, String), + #[error("Failed to get control descriptor for {0}: {1}")] + FailedToGetControlDescriptor(ControlId, String), + #[error("Could not shutdown {backend} device {device}: {error}")] + ShutdownError { + backend: Backends, + device: CameraIndex, error: String, }, + + // Stream Related Errors #[error("Could not open device stream: {0}")] OpenStreamError(String), - #[error("Could not capture frame: {0}")] - ReadFrameError(String), - #[error("Could not process frame {src} to {destination}: {error}")] - ProcessFrameError { - src: FrameFormat, - destination: String, - error: String, - }, + #[error("Error occured during stream: {0:?}")] + StreamError(StreamError), #[error("Could not stop stream: {0}")] StreamShutdownError(String), + #[error("Device no longer exists: {0}")] + DeviceNoLongerExists(CameraIndex), + + // Not-Implemented Errors #[error("This operation is not supported by backend {0}.")] UnsupportedOperationError(Backends), #[error("This operation is not implemented yet: {0}")] NotImplementedError(String), - #[error("Failed To Convert: {0}")] - ConversionError(String), - #[error("Permission denied by user.")] - PermissionDenied, + + // Decoders #[error("Failed to decode: {0}")] Decoder(String), #[error("Unsupported FrameFormat: {0}")] @@ -98,23 +115,17 @@ impl NokhwaError { } } -// -// pub enum InitializeError {} -// -// pub enum QueryBackendError {} -// -// pub enum OpenDeviceError {} -// -// pub enum QueryDeviceError {} -// -// pub enum GetPropertyError {} -// -// pub enum SetPropertyError {} -// -// pub enum OpenStreamError {} -// -// pub enum CloseStreamError {} -// -// pub enum FrameError {} -// -// pub enum DecoderError {} +/// Errors that may occur during a stream +#[derive(Error, Debug, Clone)] +pub enum StreamError { + NotReady, + NoLongerExists, + StreamInvalidated, + Other(String), +} + +impl Display for StreamError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} diff --git a/nokhwa-core/src/frame_buffer.rs b/nokhwa-core/src/frame_buffer.rs index 3b0ad9b..5f9e4d0 100644 --- a/nokhwa-core/src/frame_buffer.rs +++ b/nokhwa-core/src/frame_buffer.rs @@ -13,75 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use crate::control::ControlValue; -pub use compact_str::CompactString; -pub use smallmap::Map; +use crate::metadata::Metadata; use std::borrow::Cow; -use std::hash::{Hash, Hasher}; use std::ops::Deref; -pub type PlatformSpecificFlag = u32; - -#[derive(Clone, Debug, Default)] -pub struct Metadata { - flags: Map, -} - -impl Metadata { - #[must_use] - pub fn new() -> Self { - Self { - flags: Map::default(), - } - } - - #[must_use] - pub fn get(&self, key: &str) -> Option<&ControlValue> { - self.flags.get(key) - } - - pub fn insert(&mut self, key: CompactString, value: ControlValue) { - self.flags.insert(key, value); - } -} - -impl Hash for Metadata { - fn hash(&self, state: &mut H) { - for (key, value) in self.flags.iter() { - state.write(key.as_bytes()); - value.hash(state); - } - } -} - -impl Deref for Metadata { - type Target = Map; - - fn deref(&self) -> &Self::Target { - &self.flags - } -} - -impl PartialEq for Metadata { - fn eq(&self, other: &Self) -> bool { - for (this_key, this_value) in self.flags.iter() { - if let Some(other_value) = other.flags.get(this_key) { - if this_value != other_value { - return false; - } - } else { - return false; - } - } - true - } -} - /// A buffer returned by a camera to accommodate custom decoding. /// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer. /// /// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames. -#[derive(Clone, Debug, Hash, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct FrameBuffer<'a> { buffer: Cow<'a, [u8]>, metadata: Option, diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 0898340..560b294 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -16,6 +16,7 @@ use ordered_float::OrderedFloat; use std::fmt::{Display, Formatter}; +pub use uuid::Uuid; macro_rules! define_frame_format_with_groups { ( @@ -209,7 +210,7 @@ impl FrameFormat { #[must_use] pub fn is_custom(&self) -> bool { if let FrameFormat::Custom(_) = self { - return true + return true; } false } @@ -224,8 +225,8 @@ impl Display for FrameFormat { #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum CustomFrameFormat { - UUID(u128), - FourCC([char; 4]), + UUID(Uuid), + FourCC([u8; 4]), U32(u32), U64(u64), F32(OrderedFloat), diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index bd62575..9d2dfcf 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -29,10 +29,11 @@ pub mod format_request; pub mod frame_buffer; pub mod frame_format; pub mod image; +pub mod metadata; +pub mod pixel_destination; pub mod platform; pub mod ranges; pub mod stream; pub mod traits; pub mod types; pub mod utils; -pub mod pixel_destination; diff --git a/nokhwa-core/src/metadata.rs b/nokhwa-core/src/metadata.rs new file mode 100644 index 0000000..5a63a02 --- /dev/null +++ b/nokhwa-core/src/metadata.rs @@ -0,0 +1,58 @@ +use std::{collections::HashSet, fmt::Display, hash::Hash}; + +pub use time::Time; +pub use uuid::Uuid; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum MetadataValue { + String(String), + Uuid(Uuid), + I64(i64), + U64(u64), + I32(i32), + U32(u32), + Byte(u8), + Timestamp(Time), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum MetadataTypes { + Size(u64), + Timestamp(Time), + PlatformBitflag32(u32), + PlatformBitflag64(u64), + Sequence(u64), + FieldOrder(u32), + Custom { name: String, value: MetadataValue }, +} + +impl Hash for MetadataTypes { + fn hash(&self, state: &mut H) { + match self { + MetadataTypes::Custom { name, .. } => name.hash(state), + meta => core::mem::discriminant(meta).hash(state), + } + } +} + +impl Display for MetadataTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MetadataTypes::Size(s) => write!(f, "Metadata Size: {s}b"), + MetadataTypes::Timestamp(time) => { + let (hour, minute, second, micro) = time.as_hms_micro(); + write!( + f, + "Metadata Timestamp: {hour}h {minute}m {second}s {micro}us" + ) + } + MetadataTypes::PlatformBitflag32(bf) => write!(f, "Metadata Bitflag 32bit: {bf:032b}"), + MetadataTypes::PlatformBitflag64(bf) => write!(f, "Metadata Bitflag 32bit: {bf:064b}"), + MetadataTypes::Sequence(seq) => write!(f, "Metadata Order in Sequence: #{seq}"), + MetadataTypes::FieldOrder(or) => write!(f, "Metadata Field Order: {or}"), + MetadataTypes::Custom { name, value } => write!(f, "Metadata {name}: {value:?}"), + } + } +} + +pub type Metadata = HashSet; diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index 963244e..95e5342 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -1,11 +1,10 @@ -use crate::camera::Camera; +use crate::camera::CameraTrait; use crate::error::NokhwaResult; use crate::types::{Backends, CameraIndex, QueriedCamera}; - pub trait PlatformTrait { const PLATFORM: Backends; - type Camera: Camera; + type Camera: CameraTrait; fn block_on_permission(&mut self) -> NokhwaResult<()>; @@ -14,33 +13,26 @@ pub trait PlatformTrait { fn query(&mut self) -> NokhwaResult>; fn open(&mut self, index: CameraIndex) -> NokhwaResult; - - fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult> - where - ::Camera: 'static, - { - self.open(index).map(|cam| Box::new(cam) as Box) - } } -#[cfg(feature = "async")] -#[cfg_attr(feature = "async", async_trait::async_trait)] -pub trait AsyncPlatformTrait: PlatformTrait { - const PLATFORM: Backends; - type AsyncCamera: crate::camera::AsyncCamera; +// #[cfg(feature = "async")] +// #[cfg_attr(feature = "async", async_trait::async_trait)] +// pub trait AsyncPlatformTrait: PlatformTrait { +// const PLATFORM: Backends; +// type AsyncCamera: crate::camera::AsyncCamera; - async fn await_permission(&mut self) -> NokhwaResult<()>; +// async fn await_permission(&mut self) -> NokhwaResult<()>; - async fn query_async(&mut self) -> NokhwaResult>; +// async fn query_async(&mut self) -> NokhwaResult>; - async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult; +// async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult; - async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult> - where - ::AsyncCamera: 'static, - { - self.open_async(index) - .await - .map(|cam| Box::new(cam) as Box) - } -} +// async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult> +// where +// ::AsyncCamera: 'static, +// { +// self.open_async(index) +// .await +// .map(|cam| Box::new(cam) as Box) +// } +// } diff --git a/nokhwa-core/src/stream.rs b/nokhwa-core/src/stream.rs index b1b33cc..c3fe631 100644 --- a/nokhwa-core/src/stream.rs +++ b/nokhwa-core/src/stream.rs @@ -1,213 +1,7 @@ -use crate::error::NokhwaError; -use crate::frame_buffer::FrameBuffer; -use crate::types::CameraFormat; -use flume::{Receiver, Sender, TryRecvError}; -use std::cell::Cell; -use std::sync::Arc; -use std::time::Duration; -use typed_builder::TypedBuilder; +use crate::{error::NokhwaError, types::CameraFormat}; -/// What receiving behaviour the stream should observe. -/// -/// Note that [`StreamHandleTrait::poll_frame`] does not respect [`StreamReceiverBehaviour::Timeout`] - -/// it will either immediately return (try once) or block until the next frame or error. -/// -/// The default behaviour is to block until a new event is sent. -#[derive(Clone, Debug, Default, PartialOrd, PartialEq)] -pub enum StreamReceiverBehaviour { - /// Blocks until a new event is sent to the Stream. - #[default] - Blocking, - /// Only waits [duration] amount of time for a new event, returning [`Event::NotReady`] otherwise. - Timeout(Duration), - /// Immediately return. If there is no event waiting for the stream, it will return an [`Event::NotReady`] instead. - Try, -} - -/// How many events a stream can hold. By default, it is **one**. -/// -/// This means that streams will be blocked until the stream handle is emptied. -#[derive(Clone, Debug, PartialOrd, PartialEq)] -pub enum StreamBounds { - Bounded(u32), - Unbounded, -} - -impl Default for StreamBounds { - fn default() -> Self { - StreamBounds::Bounded(1) - } -} - -#[derive(Clone, Default, Debug, PartialOrd, PartialEq)] -pub enum ControlFlowOnOther { - Continue, - #[default] - Break, -} - -/// Configuration for a [`StreamHandle`]. -#[derive(Clone, Debug, Default, PartialOrd, PartialEq, TypedBuilder)] -pub struct StreamConfiguration { - #[builder(default)] - pub receiver: StreamReceiverBehaviour, - #[builder(default)] - pub bound: StreamBounds, - #[builder(default)] - pub on_other: ControlFlowOnOther, -} - -/// Possible events to receive from an active stream. -#[derive(Clone, Debug, PartialEq)] -pub enum Event<'a> { - /// A new frame. - NewFrame(FrameBuffer<'a>), - /// Camera Format Changed. - /// - /// This will usually require the reset of a buffer, or be followed by a [`Event::Terminated`], - /// depending on the backend used. - FormatChange(CameraFormat), - /// This stream is not ready for another event. This is **never** sent by the stream itself, but - /// instead a [`StreamHandle`] construct for when the user sets [`StreamReceiverBehaviour`] to either - /// [`StreamReceiverBehaviour::Timeout`] or [`StreamReceiverBehaviour::Try`] but the stream does not - /// have the data ready. - /// - /// (This can be ignored when iterating, or using the [`StreamReceiverBehaviour::Blocking`] approach.) - NotReady, - /// The stream will be ended shortly. Users should call [`StreamHandleTrait::close_stream`] afterwards. - Terminating, - /// The stream is closed. - Closed, - /// An error from the driver - Error(String), - /// Some other message sent by the driver. This can be ignored, although logging this is preferable. - Other(String), -} - -/// Represents a handle to a currently open stream. -/// -/// Streams are only valid as long as the camera is live. Any Stream that is living past a camera -/// is invalid to use. (This doesn't cause UB, it will just kindly tell you that the stream has -/// already closed.) -/// -/// Streams may unexpectedly close due to unforeseen consequences e.g. webcam undergoes spontaneous -/// deconstruction. -/// -/// The async methods [`StreamHandle::poll_event`] and [`StreamHandle::poll_frame`] **do not** respect the [`StreamReceiverBehaviour`] setting. -/// -/// You may also close the stream from the handle side using -#[derive(Debug)] -pub struct StreamHandle<'a> { - frame: Receiver>, - control: Arc>, - configuration: StreamConfiguration, - format: Cell, -} - -impl<'a> StreamHandle<'a> { - /// You shouldn't be here. - #[must_use] - pub fn new( - recv: Receiver>, - control: Arc>, - configuration: StreamConfiguration, - format: CameraFormat, - ) -> Self { - Self { - frame: recv, - control, - configuration, - format: Cell::new(format), - } - } - - pub fn configuration(&self) -> &StreamConfiguration { - &self.configuration - } - - pub fn format(&self) -> CameraFormat { - self.format.get() - } - - pub fn next_event(&self) -> Result, NokhwaError> { - let event = match self.configuration.receiver { - StreamReceiverBehaviour::Blocking => { - self.frame.recv().unwrap_or_else(|_| Event::Closed) - } - StreamReceiverBehaviour::Timeout(time) => self - .frame - .recv_timeout(time) - .unwrap_or_else(|_| Event::NotReady), - StreamReceiverBehaviour::Try => self.frame.try_recv().unwrap_or_else(|why| match why { - TryRecvError::Empty => Event::NotReady, - TryRecvError::Disconnected => Event::Closed, - }), - }; - - if let Event::FormatChange(fmt) = event { - self.format.set(fmt); - } - - Ok(event) - } - - pub fn next_frame(&self) -> Result, NokhwaError> { - loop { - let event = self.next_event()?; - match event { - Event::NewFrame(f) => return Ok(f), - Event::Terminating | Event::Closed => { - let _ = self.control.try_send(()); - return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string())); - } - Event::Other(why) => { - if self.configuration.on_other == ControlFlowOnOther::Break { - return Err(NokhwaError::ReadFrameError(why)); - } - } - Event::Error(e) => return Err(NokhwaError::ReadFrameError(e.clone())), - _ => {} - } - } - } - - #[cfg(feature = "async")] - pub async fn poll_event(&self) -> Result, NokhwaError> { - Ok(self.frame.recv_async().await.map_or_else( - |_| Event::Closed, - |e| { - if let Event::FormatChange(fmt) = e { - self.format.set(fmt); - } - e - }, - )) - } - - // TODO: a smarter implementation? maybe? - #[cfg(feature = "async")] - pub async fn poll_next_frame(&self) -> Result, NokhwaError> { - loop { - let event = self.poll_event().await?; - match event { - Event::NewFrame(f) => return Ok(f), - Event::Terminating | Event::Closed => { - let _ = self.control.try_send(()); - return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string())); - } - Event::Other(why) => { - if let ControlFlowOnOther::Break = self.configuration.on_other { - return Err(NokhwaError::ReadFrameError(why)); - } - } - _ => {} - } - } - } -} - -impl Drop for StreamHandle<'_> { - fn drop(&mut self) { - let _ = self.control.try_send(()); - } +pub trait StreamTrait { + fn current_format(&self) -> &CameraFormat; + + fn stop_stream(self) -> Result<(), NokhwaError>; } diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 7689673..2bed268 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -1,11 +1,10 @@ use crate::ranges::RangeItem; use crate::utils::Distance; use crate::{error::NokhwaError, frame_format::FrameFormat}; -use num_rational::Rational32; +use num_rational::Ratio; use num_traits::FromPrimitive; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; -use std::num::NonZeroI32; use std::ops::{Div, Rem}; use std::{ cmp::Ordering, @@ -22,7 +21,7 @@ use std::{ pub enum CameraIndex { Index(u32), String(String), - Stable(String) + Stable(String), } impl CameraIndex { @@ -32,9 +31,9 @@ impl CameraIndex { pub fn as_index(&self) -> Result { match self { CameraIndex::Index(i) => Ok(*i), - CameraIndex::String(s) | CameraIndex::Stable(s) => s - .parse::() - .map_err(|why| NokhwaError::GeneralError(why.to_string())), + CameraIndex::String(s) | CameraIndex::Stable(s) => { + s.parse::().map_err(NokhwaError::IndexParsingFailed) + } } } @@ -44,7 +43,7 @@ impl CameraIndex { match self { CameraIndex::Index(i) => i.to_string(), CameraIndex::String(s) | CameraIndex::Stable(s) => s.clone(), - } + } } /// Returns true if this [`CameraIndex`] contains an [`CameraIndex::Index`] @@ -108,10 +107,7 @@ impl Resolution { #[must_use] // TODO: make this height and width. pub const fn new(width: u32, height: u32) -> Self { - Resolution { - width, - height, - } + Resolution { width, height } } /// Get the width of Resolution @@ -230,43 +226,43 @@ impl RangeItem for Resolution { #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct FrameRate { - rational: Rational32, + rational: Ratio, } impl FrameRate { #[must_use] - pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self { + pub const fn new(numerator: u32, denominator: u32) -> Self { Self { - rational: Rational32::new_raw(numerator, denominator.get()), + rational: Ratio::::new_raw(numerator, denominator), } } #[must_use] - pub const fn from_fps(fps: i32) -> Self { + pub const fn from_fps(fps: u32) -> Self { Self { - rational: Rational32::new_raw(fps, 1), + rational: Ratio::::new_raw(fps, 1), } } #[must_use] - pub fn numerator(&self) -> i32 { + pub fn numerator(&self) -> u32 { *self.rational.numer() } #[must_use] - pub fn denominator(&self) -> i32 { + pub fn denominator(&self) -> u32 { *self.rational.denom() } #[must_use] - pub fn as_raw(&self) -> &Rational32 { + pub fn as_raw(&self) -> &Ratio { &self.rational } #[must_use] pub fn approximate_float(&self) -> Option { - let numerator_float = f32::from_i32(self.numerator())?; - let denominator_float = f32::from_i32(self.denominator())?; + let numerator_float = f32::from_u32(self.numerator())?; + let denominator_float = f32::from_u32(self.denominator())?; Some(numerator_float / denominator_float) } @@ -274,7 +270,7 @@ impl FrameRate { impl Default for FrameRate { fn default() -> Self { - FrameRate::new(30, NonZeroI32::new(1).unwrap()) + FrameRate::new(30, 1) } } @@ -319,11 +315,11 @@ impl Rem for FrameRate { impl RangeItem for FrameRate { const ZERO: Self = FrameRate::from_fps(0); const MIN: Self = FrameRate::from_fps(0); - const MAX: Self = FrameRate::from_fps(i32::MAX); + const MAX: Self = FrameRate::from_fps(u32::MAX); } -impl From for FrameRate { - fn from(value: Rational32) -> Self { +impl From> for FrameRate { + fn from(value: Ratio) -> Self { FrameRate { rational: value } } } @@ -430,7 +426,7 @@ impl Display for CameraFormat { /// Information about a Camera e.g. its name. /// `description` and `misc` may contain information that may differ from backend to backend. Refer to each backend for details. -/// `stable_id` contains the stable ID that may be used to reopen the same device. +/// `stable_id` contains the stable ID that may be used to reopen the same device. #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct CameraInformation { @@ -446,7 +442,12 @@ impl CameraInformation { // OK, i just checkeed back on this code. WTF was I on when I wrote `&(impl AsRef + ?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: String, description: String, misc: String, stable_id: Option) -> Self { + pub fn new( + human_name: String, + description: String, + misc: String, + stable_id: Option, + ) -> Self { CameraInformation { human_name, description, @@ -475,7 +476,8 @@ impl CameraInformation { &self.misc } - #[must_use] pub fn stable_id(&self) -> Option<&str> { + #[must_use] + pub fn stable_id(&self) -> Option<&str> { self.stable_id.as_deref() } } @@ -509,5 +511,5 @@ impl Display for Backends { #[derive(Clone, Debug, PartialOrd, PartialEq)] pub struct QueriedCamera { pub index: CameraIndex, - pub information: CameraInformation + pub information: CameraInformation, } diff --git a/nokhwa-decoders/Cargo.toml b/nokhwa-decoders/Cargo.toml index c50dcb4..95852b9 100644 --- a/nokhwa-decoders/Cargo.toml +++ b/nokhwa-decoders/Cargo.toml @@ -8,8 +8,7 @@ ffmpeg = ["ffmpeg-the-third"] yuyv = ["dcv-color-primitives", "yuv"] mjpeg = ["zune-jpeg", "zune-core"] luma = ["nokhwa-iter-extensions", "itermore"] -static = ["ffmpeg-the-third/static"] -#async = [] +#ffmpeg-static = ["ffmpeg-the-third/static"] [dependencies] bytemuck = "1.23" @@ -50,4 +49,4 @@ optional = true [dev-dependencies.image] workspace = true -features = ["png", "jpeg", "avif"] +features = ["png", "jpeg"] diff --git a/nokhwa-decoders/src/ffmpeg.rs b/nokhwa-decoders/src/ffmpeg.rs index 78db23a..8e11d85 100644 --- a/nokhwa-decoders/src/ffmpeg.rs +++ b/nokhwa-decoders/src/ffmpeg.rs @@ -54,8 +54,8 @@ fn create_video(config: &FfmpegConfig) -> Result { })?; let frame_rate = AVRational { - num: config.frame_rate.numerator(), - den: config.frame_rate.denominator(), + num: config.frame_rate.numerator() as i32, + den: config.frame_rate.denominator() as i32, }; let codec_i32 = unsafe { transmute::(AVCodecID::from(id)) }; let intermediate_config = IntermediateDecoderConfig { @@ -335,7 +335,7 @@ fn convert_format_to_codec_id(frame_format: &FrameFormat) -> Option { } match frame_format { - FrameFormat::H265 => Some(Id::H265), + FrameFormat::H265 => Some(Id::HEVC), FrameFormat::H264 => Some(Id::H264), FrameFormat::AVC1 => Some(Id::H264), FrameFormat::H263 => Some(Id::H263), @@ -652,7 +652,7 @@ const fn is_little_endian() -> bool { #[cfg(test)] mod test { - use ffmpeg_the_third::{codec::Context, format::input, media::Type, threading::Config}; + use ffmpeg_the_third::format::input; use image::{ImageFormat, Rgb}; use nokhwa_core::{ decoder::Decoder, @@ -712,13 +712,13 @@ mod test { pub fn test_h265() { ffmpeg_the_third::init().unwrap(); - let file = "test_images/ffmpeg/h265/out.h265"; + let file = "test_images/ffmpeg/h265/bitstream.265"; let output_dir = "test_images/ffmpeg/h265/out"; let decoder_cfg = FfmpegConfig { custom_frame_format_map: None, frame_format: FrameFormat::H265, - resolution: Resolution::new(498, 348), + resolution: Resolution::new(480, 270), frame_rate: FrameRate::from_fps(30), parallelism: Parallelism::default(), ffmpeg_codec_low_level: FfmpegDecoderConfig::default(), diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/HelloCharlotte-13oxf29.mp4 b/nokhwa-decoders/test_images/ffmpeg/h265/HelloCharlotte-13oxf29.mp4 new file mode 100644 index 0000000..7455585 Binary files /dev/null and b/nokhwa-decoders/test_images/ffmpeg/h265/HelloCharlotte-13oxf29.mp4 differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/bitstream.265 b/nokhwa-decoders/test_images/ffmpeg/h265/bitstream.265 new file mode 100644 index 0000000..a148d9a Binary files /dev/null and b/nokhwa-decoders/test_images/ffmpeg/h265/bitstream.265 differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/i-have-your-baby-.mp4 b/nokhwa-decoders/test_images/ffmpeg/h265/i-have-your-baby-.mp4 deleted file mode 100644 index 970e070..0000000 Binary files a/nokhwa-decoders/test_images/ffmpeg/h265/i-have-your-baby-.mp4 and /dev/null differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/out.h265 b/nokhwa-decoders/test_images/ffmpeg/h265/out.h265 deleted file mode 100644 index 38913f9..0000000 Binary files a/nokhwa-decoders/test_images/ffmpeg/h265/out.h265 and /dev/null differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/output.mkv b/nokhwa-decoders/test_images/ffmpeg/h265/output.mkv new file mode 100644 index 0000000..16499e3 Binary files /dev/null and b/nokhwa-decoders/test_images/ffmpeg/h265/output.mkv differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/output.mp4 b/nokhwa-decoders/test_images/ffmpeg/h265/output.mp4 deleted file mode 100644 index 5a83f3f..0000000 Binary files a/nokhwa-decoders/test_images/ffmpeg/h265/output.mp4 and /dev/null differ diff --git a/nokhwa-decoders/test_images/ffmpeg/h265/testh265.mkv b/nokhwa-decoders/test_images/ffmpeg/h265/testh265.mkv deleted file mode 100644 index b192850..0000000 Binary files a/nokhwa-decoders/test_images/ffmpeg/h265/testh265.mkv and /dev/null differ