diff --git a/flake.lock b/flake.lock index aa4bebd..b2d784f 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1750215678, - "narHash": "sha256-Rc/ytpamXRf6z8UA2SGa4aaWxUXRbX2MAWIu2C8M+ok=", + "lastModified": 1751498133, + "narHash": "sha256-QWJ+NQbMU+NcU2xiyo7SNox1fAuwksGlQhpzBl76g1I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5395fb3ab3f97b9b7abca147249fa2e8ed27b192", + "rev": "d55716bb59b91ae9d1ced4b1ccdea7a442ecbfdb", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1750387093, - "narHash": "sha256-MgL1+yNVcSD6OlzSmKt5GS4RmAQnNCjckjgPC1hmMPg=", + "lastModified": 1751510438, + "narHash": "sha256-m8PjOoyyCR4nhqtHEBP1tB/jF+gJYYguSZmUmVTEAQE=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "517e9871d182346b53bb7f23fed00810c14db396", + "rev": "7f415261f298656f8164bd636c0dc05af4e95b6b", "type": "github" }, "original": { diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index 85a072d..831f1f7 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"] +serialize = ["serde", "ordered-float/serde"] wgpu = ["wgpu-types"] opencv-mat = ["opencv", "opencv/clang-runtime"] docs-features = ["serialize", "wgpu-types"] @@ -29,6 +29,8 @@ typed-builder = "0.21" compact_str = "0.9" bytemuck = "1.23" smallmap = "1.4" +constcat = "0.6" +paste = "1.0" [dependencies.num-rational] version = "0.4" diff --git a/nokhwa-core/src/camera.rs b/nokhwa-core/src/camera.rs index 19d3065..7602198 100644 --- a/nokhwa-core/src/camera.rs +++ b/nokhwa-core/src/camera.rs @@ -9,41 +9,42 @@ use std::sync::Arc; pub trait Setting { /// # Errors - /// Will error on + /// Will error on fn enumerate_formats(&self) -> Result, NokhwaError>; /// # Errors - /// Will error on + /// Will error on fn enumerate_resolution_and_frame_rates( &self, frame_format: FrameFormat, ) -> Result>, NokhwaError>; /// # Errors - /// Will error on + /// Will error on fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - fn control_ids(&self) -> Keys; + fn control_ids(&self) -> Keys<'_, ControlId, ControlDescription>; - fn control_descriptions(&self) -> Values; + fn control_descriptions(&self) -> Values<'_, ControlId, ControlDescription>; - fn control_values(&self) -> Values; + 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 + /// Will error on fn set_control(&mut self, property: &ControlId, value: ControlValue) -> Result<(), NokhwaError>; /// # Errors - /// Will error on + /// 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>; @@ -53,7 +54,7 @@ pub trait AsyncSetting { ) -> Result>, NokhwaError>; async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>; - + async fn set_property_async( &mut self, property: &ControlId, @@ -68,7 +69,7 @@ pub trait Capture { fn open_stream( &mut self, stream_configuration: Option, - ) -> Result, NokhwaError>; + ) -> Result>, NokhwaError>; // Implementations MUST be multi-close tolerant. /// # Errors @@ -77,11 +78,12 @@ pub trait Capture { } #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncStream { - async fn open_stream_async( + async fn open_stream_async<'a>( &mut self, stream_configuration: Option, - ) -> Result; + ) -> Result, NokhwaError>; async fn close_stream_async(&mut self) -> Result<(), NokhwaError>; } diff --git a/nokhwa-core/src/codec.rs b/nokhwa-core/src/codec.rs index f2d8956..c07593f 100644 --- a/nokhwa-core/src/codec.rs +++ b/nokhwa-core/src/codec.rs @@ -8,27 +8,27 @@ pub trait Codec { type Input<'a>; - type Output; + type Output<'a>; type WrittenMeta: Clone + Debug; /// # Errors - /// Errors are decoder specific. + /// Errors are decoder specific. fn allowed_formats(&self) -> Result<&[FrameFormat], NokhwaError>; fn config(&self) -> &Self::Config; /// # Errors - /// Errors are decoder specific. + /// Errors are decoder specific. fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError>; /// # Errors - /// Errors are decoder specific. + /// Errors are decoder specific. fn send_item(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError>; fn receive_decoded_item( &mut self, - writing_output: &mut Self::Output, + writing_output: &mut Self::Output<'_>, ) -> Result; fn preferred_buffer_min_size( @@ -40,15 +40,15 @@ pub trait Codec { } #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait CodecAsync: Codec { - - async fn allowed_formats_async(&self) -> Result<&[FrameFormat], NokhwaError> { + async fn allowed_formats_async<'a>(&'a self) -> Result<&'a [FrameFormat], NokhwaError> { self.allowed_formats() } async fn set_format_async(&self, format: CameraFormat) -> Result<(), NokhwaError>; - async fn config_async(&self) -> &Self::Config { + async fn config_async<'a>(&'a self) -> &'a Self::Config { self.config() } @@ -56,11 +56,11 @@ pub trait CodecAsync: Codec { self.set_config(config) } - fn send_item_async(&mut self, input: &Self::Input) -> Result<(), NokhwaError>; + fn send_item_async(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError>; fn receive_decoded_item_async( &mut self, - writing_output: &mut Self::Output, + writing_output: &mut Self::Output<'_>, ) -> Result, NokhwaError>; async fn deinitialize_async(&mut self) -> Result<(), NokhwaError> { diff --git a/nokhwa-core/src/control.rs b/nokhwa-core/src/control.rs index 4fa8f5d..a89e095 100644 --- a/nokhwa-core/src/control.rs +++ b/nokhwa-core/src/control.rs @@ -61,15 +61,16 @@ pub struct Controls { impl Controls { /// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls` - #[must_use] pub fn new( + #[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) { - if !description.validate(value) { - return None; - } + if let Some(description) = device_controls.get(id) + && !description.validate(value) + { + return None; } } @@ -79,11 +80,13 @@ impl Controls { }) } - #[must_use] pub fn empty() -> Self { + #[must_use] + pub fn empty() -> Self { Self::default() } - #[must_use] pub fn unchecked_new( + #[must_use] + pub fn unchecked_new( device_controls: HashMap, device_values: HashMap, ) -> Self { @@ -93,23 +96,28 @@ impl Controls { } } - #[must_use] pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> { + #[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> { + #[must_use] + pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> { self.values.get(control_id) } - #[must_use] pub fn descriptions(&self) -> Values { + #[must_use] + pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> { self.descriptions.values() } - #[must_use] pub fn values(&self) -> Values { + #[must_use] + pub fn values(&self) -> Values<'_, ControlId, ControlValue> { self.values.values() } - #[must_use] pub fn ids(&self) -> Keys { + #[must_use] + pub fn ids(&self) -> Keys<'_, ControlId, ControlDescription> { self.descriptions.keys() } @@ -119,11 +127,11 @@ impl Controls { 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(), - }); - }; + 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 { @@ -163,15 +171,16 @@ pub struct ControlDescription { } impl ControlDescription { - #[must_use] pub fn new( + #[must_use] + pub fn new( control_flags: HashSet, control_value_descriptor: ControlValueDescriptor, default_value: Option, ) -> Option { - if let Some(default) = &default_value { - if !control_value_descriptor.validate(default) { - return None; - } + if let Some(default) = &default_value + && !control_value_descriptor.validate(default) + { + return None; } Some(Self { @@ -181,7 +190,8 @@ impl ControlDescription { }) } - #[must_use] pub fn new_unchecked( + #[must_use] + pub fn new_unchecked( control_flags: HashSet, control_value_descriptor: ControlValueDescriptor, default_value: Option, @@ -193,15 +203,18 @@ impl ControlDescription { } } - #[must_use] pub fn flags(&self) -> &HashSet { + #[must_use] + pub fn flags(&self) -> &HashSet { &self.flags } - #[must_use] pub fn descriptor(&self) -> &ControlValueDescriptor { + #[must_use] + pub fn descriptor(&self) -> &ControlValueDescriptor { &self.descriptor } - #[must_use] pub fn default_value(&self) -> &Option { + #[must_use] + pub fn default_value(&self) -> &Option { &self.default_value } @@ -213,7 +226,8 @@ impl ControlDescription { self.flags.remove(&flag) } - #[must_use] pub fn validate(&self, value: &ControlValue) -> bool { + #[must_use] + pub fn validate(&self, value: &ControlValue) -> bool { self.descriptor.validate(value) } } @@ -261,7 +275,8 @@ pub enum ControlValueDescriptor { } impl ControlValueDescriptor { - #[must_use] pub fn validate(&self, value: &ControlValue) -> bool { + #[must_use] + pub fn validate(&self, value: &ControlValue) -> bool { match self { ControlValueDescriptor::Null => { if let &ControlValue::Null = value { @@ -342,16 +357,20 @@ pub enum ControlValue { } impl ControlValue { - #[must_use] pub fn is_primitive(&self) -> bool { - matches!(self, ControlValue::Null - | ControlValue::Integer(_) - | ControlValue::BitMask(_) - | ControlValue::Float(_) - | ControlValue::String(_) - | ControlValue::Boolean(_) - | ControlValue::Binary(_) - | ControlValue::Area { .. } - | ControlValue::Orientation(_)) + #[must_use] + pub fn is_primitive(&self) -> bool { + matches!( + self, + ControlValue::Null + | ControlValue::Integer(_) + | ControlValue::BitMask(_) + | ControlValue::Float(_) + | ControlValue::String(_) + | ControlValue::Boolean(_) + | ControlValue::Binary(_) + | ControlValue::Area { .. } + | ControlValue::Orientation(_) + ) } // pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool { @@ -370,7 +389,8 @@ impl ControlValue { // false // } - #[must_use] pub fn same_type(&self, other: &ControlValue) -> bool { + #[must_use] + pub fn same_type(&self, other: &ControlValue) -> bool { match self { ControlValue::Null => { if let ControlValue::Null = other { diff --git a/nokhwa-core/src/decoder.rs b/nokhwa-core/src/decoder.rs index 1fbce55..e82f705 100644 --- a/nokhwa-core/src/decoder.rs +++ b/nokhwa-core/src/decoder.rs @@ -1,13 +1,14 @@ use crate::error::NokhwaError; use crate::frame_buffer::FrameBuffer; -use crate::types::CameraFormat; -use std::fmt::Debug; -pub use image::{ImageBuffer, Pixel, Primitive}; use crate::image::{DecodedImage, NonFloatScalarWidth}; +use crate::types::{CameraFormat, Resolution}; +pub use image::{ImageBuffer, Pixel, Primitive}; +use std::fmt::Debug; pub trait Decoder { - type Config: Clone + Debug + TryFrom; - type OutputMeta: Debug; + type Config: Clone + Debug; + type OutputMeta: Clone + Debug; + type DestinationFormatHint: Clone + Debug; fn config(&self) -> &Self::Config; @@ -17,18 +18,34 @@ pub trait Decoder { &mut self, to_decode: FrameBuffer, buffer: impl AsMut<[u8]>, + destination_format_hint: Option, ) -> Result; fn decode_to_pixel_buffer( &mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>, - ) -> Result - where

::Subpixel: NonFloatScalarWidth; + ) -> Result + where +

::Subpixel: NonFloatScalarWidth; fn decode( &mut self, to_decode: FrameBuffer, ) -> Result, NokhwaError> - where

::Subpixel: NonFloatScalarWidth; + where +

::Subpixel: NonFloatScalarWidth; + + fn output_decoder_min_size_pixel

(&self, resolution: Resolution) -> usize where + P: Pixel, +

::Subpixel: NonFloatScalarWidth { + let channels = P::CHANNEL_COUNT as usize; + let width_bytes = <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES; + let resolution_mult = resolution.height() * resolution.width(); + (resolution_mult as usize) * (width_bytes as usize) * channels + } + + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize; + + fn buffer_takes_destination_hint(&self) -> bool; } diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index f777f85..3211231 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -64,6 +64,22 @@ pub enum NokhwaError { PermissionDenied, #[error("Failed to decode: {0}")] Decoder(String), + #[error("Unsupported FrameFormat: {0}")] + DecoderUnsupportedFrameFormat(FrameFormat), + #[error("Unsupported pixel configuration {0} with width {1}b.")] + DecoderUnsupportedDestinationPixelFormat(&'static str, u32), + #[error("Bad decoder configuration: {0}")] + DecoderInvalidConfiguration(String), + #[error("Failed to initialize decoder: {0}")] + DecoderInitializationError(String), + #[error("Bad frame sent to the decoder: {0}")] + DecoderInvalidFrameData(String), + #[error("Bad buffer sent to decoder, did not write: {0}")] + DecoderInvalidBuffer(String), + #[error("You need to pass in a destination hint, it is not optional for this decoder.")] + DecoderDestinationHintRequired, + #[error("Decoder already deinitialized. Unusable, please make a new decoder.")] + DecoderAlreadyDeinitialized } // // pub enum InitializeError {} diff --git a/nokhwa-core/src/format_request.rs b/nokhwa-core/src/format_request.rs index 0d09499..3be128f 100644 --- a/nokhwa-core/src/format_request.rs +++ b/nokhwa-core/src/format_request.rs @@ -39,7 +39,8 @@ pub struct FormatRequest { } impl FormatRequest { - #[must_use] pub fn new( + #[must_use] + pub fn new( format_request_type: FormatRequestType, allowed_frame_formats: Vec, ) -> Self { @@ -49,11 +50,13 @@ impl FormatRequest { } } - #[must_use] pub fn best<'a>(&self, camera_formats: &'a [CameraFormat]) -> Option<&'a CameraFormat> { + #[must_use] + pub fn best<'a>(&self, camera_formats: &'a [CameraFormat]) -> Option<&'a CameraFormat> { camera_formats.first() } - #[must_use] pub fn sort_foramts(&self, mut camera_formats: Vec) -> Vec { + #[must_use] + pub fn sort_foramts(&self, mut camera_formats: Vec) -> Vec { if camera_formats.is_empty() { return camera_formats; } @@ -77,35 +80,35 @@ impl FormatRequest { camera_formats .into_iter() - .filter(|fmt| self.allowed_frame_formats.contains(fmt.format())) + .filter(|fmt| self.allowed_frame_formats.contains(&fmt.format())) .filter(|cam_fmt| { if let Some(res_range) = resolution { - return res_range.validate(cam_fmt.resolution()); + return res_range.validate(&cam_fmt.resolution()); } if let Some(frame_rate_range) = frame_rate { - return frame_rate_range.validate(cam_fmt.frame_rate()); + return frame_rate_range.validate(&cam_fmt.frame_rate()); } true }) .collect() } FormatRequestType::HighestFrameRate { frame_rate } => { - camera_formats.sort_by(|a, b| a.frame_rate().cmp(b.frame_rate())); + camera_formats.sort_by_key(CameraFormat::frame_rate); camera_formats .into_iter() - .filter(|fmt| self.allowed_frame_formats.contains(fmt.format())) - .filter(|a| frame_rate.validate(a.frame_rate())) + .filter(|fmt| self.allowed_frame_formats.contains(&fmt.format())) + .filter(|a| frame_rate.validate(&a.frame_rate())) .collect() } FormatRequestType::HighestResolution { resolution } => { - camera_formats.sort_by(|a, b| a.resolution().cmp(b.resolution())); + camera_formats.sort_by_key(CameraFormat::frame_rate); camera_formats .into_iter() - .filter(|fmt| self.allowed_frame_formats.contains(fmt.format())) - .filter(|a| resolution.validate(a.resolution())) + .filter(|fmt| self.allowed_frame_formats.contains(&fmt.format())) + .filter(|a| resolution.validate(&a.resolution())) .collect() } FormatRequestType::Exact { @@ -113,8 +116,8 @@ impl FormatRequest { frame_rate, } => camera_formats .into_iter() - .filter(|fmt| self.allowed_frame_formats.contains(fmt.format())) - .filter(|a| resolution.eq(a.resolution()) && frame_rate.eq(a.frame_rate())) + .filter(|fmt| self.allowed_frame_formats.contains(&fmt.format())) + .filter(|a| resolution.eq(&a.resolution()) && frame_rate.eq(&a.frame_rate())) .collect(), FormatRequestType::Any => { // return as-is @@ -124,7 +127,7 @@ impl FormatRequest { } } -#[must_use] +#[must_use] #[allow(clippy::cast_precision_loss)] pub fn format_distance_to_point( resolution: &Option, @@ -132,13 +135,13 @@ pub fn format_distance_to_point( format: &CameraFormat, ) -> f32 { let frame_rate_distance = match frame_rate { - Some(f_point) => (format.frame_rate() - f_point) + Some(f_point) => (&format.frame_rate() - f_point) .approximate_float() .unwrap_or(f32::INFINITY) .abs(), None => 0_f32, }; - + let resolution_point_distance = match resolution { Some(res_pt) => format.resolution().distance_from(res_pt) as f32, None => 0_f32, diff --git a/nokhwa-core/src/frame_buffer.rs b/nokhwa-core/src/frame_buffer.rs index 0734cd6..5921124 100644 --- a/nokhwa-core/src/frame_buffer.rs +++ b/nokhwa-core/src/frame_buffer.rs @@ -14,11 +14,11 @@ * limitations under the License. */ use crate::control::ControlValue; +pub use compact_str::CompactString; +pub use smallmap::Map; use std::borrow::Cow; use std::hash::{Hash, Hasher}; use std::ops::Deref; -pub use compact_str::CompactString; -pub use smallmap::Map; pub type PlatformSpecificFlag = u32; @@ -28,13 +28,15 @@ pub struct Metadata { } impl Metadata { - #[must_use] pub fn new() -> Self { + #[must_use] + pub fn new() -> Self { Self { flags: Map::default(), } } - #[must_use] pub fn get(&self, key: &str) -> Option<&ControlValue> { + #[must_use] + pub fn get(&self, key: &str) -> Option<&ControlValue> { self.flags.get(key) } @@ -80,26 +82,48 @@ impl PartialEq for Metadata { /// /// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames. #[derive(Clone, Debug, Hash, PartialEq)] -pub struct FrameBuffer { - buffer: Cow<'static, [u8]>, +pub struct FrameBuffer<'a> { + buffer: Cow<'a, [u8]>, metadata: Option, } -impl FrameBuffer { +impl<'a> FrameBuffer<'a> { /// Creates a new buffer with a [`&[u8]`]. #[must_use] #[inline] - pub fn new(buffer: Cow<'static, [u8]>, metadata: Option) -> Self { + pub fn new(buffer: Cow<'a, [u8]>, metadata: Option) -> Self { Self { buffer, metadata } } /// Get the data of this buffer. #[must_use] - pub fn buffer(&self) -> &[u8] { + pub fn buffer(&'a self) -> &'a [u8] { &self.buffer } - #[must_use] pub fn consume(self) -> (Cow<'static, [u8]>, Option) { + #[must_use] + pub fn len(&self) -> usize { + self.buffer.len() + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.buffer.is_empty() + } + + #[must_use] + pub fn deep_copy(&self) -> Self { + Self { + buffer: match &self.buffer { + Cow::Borrowed(b) => Cow::Owned(b.to_vec()), + Cow::Owned(o) => Cow::Owned(o.clone()), + }, + metadata: self.metadata.clone(), + } + } + + #[must_use] + pub fn consume(self) -> (Cow<'a, [u8]>, Option) { (self.buffer, self.metadata) } @@ -107,5 +131,18 @@ impl FrameBuffer { pub fn metadata(&self) -> Option<&Metadata> { self.metadata.as_ref() } - +} + +impl AsRef<[u8]> for FrameBuffer<'_> { + fn as_ref(&self) -> &[u8] { + self.buffer.as_ref() + } +} + +impl Deref for FrameBuffer<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.buffer.as_ref() + } } diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 03fe44e..69ab70d 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -14,75 +14,22 @@ * limitations under the License. */ -use std::fmt::{Display, Formatter}; use ordered_float::OrderedFloat; -// /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`. -// /// Note that endianness is determined by the native machine (or the driver itself). -// #[derive(Clone, Debug, Hash, PartialOrd, PartialEq)] -// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -// #[non_exhaustive] -// pub enum FrameFormat { -// // Compressed Formats -// -// -// // YCbCr Formats -// -// // 8 bit per pixel, 4:4:4 -// Ayuv444, -// -// // -> 4:2:2 -// Yuyv422, // AKA YUY2 -// Uyvy422, // UYUV -// Yvyu422, -// Yv12, -// -// // 4:2:0 -// Nv12, -// Nv21, -// I420, -// -// // 16:1:1 -// Yvu9, -// -// // Grayscale Formats -// Luma8, -// Luma16, -// -// // Depth -// Depth16, -// -// // RGB Formats -// Rgb332, -// Rgb888, -// RgbA8888, -// ARgb8888, -// RgbX1010102, -// RgbA1010102, -// ARgb1010102, -// -// -// Bgr888, -// BgrA8888, -// Bgr121212, -// BgrA1212121212, -// -// Bgr161616, -// Bgr16161616, -// -// -// // Bayer Formats -// Bayer8, -// Bayer16, -// -// // Custom -// Custom(CustomFrameFormat), -// } +use std::fmt::{Display, Formatter}; macro_rules! define_frame_format_with_groups { ( $( - $group_name:ident => [ - $($format:ident),* $(,)? + $classifier:ident [ + $( + $sub_classifier:ident [ + $( + $group_name:expr => [ + $($format:ident),* $(,)? + ] + ),* $(,)? + ] + ),* $(,)? ] ),* $(,)? ) => { @@ -98,247 +45,166 @@ macro_rules! define_frame_format_with_groups { #[allow(non_camel_case_types)] pub enum FrameFormat { $( - $($format,)* + $( + $($($format,)*)* + )* )* Custom(CustomFrameFormat), } - impl FrameFormat { + paste::paste! { + impl FrameFormat { $( - pub const $group_name: &'static [FrameFormat] = &[ - $(FrameFormat::$format),* - ]; - )* - pub const ALL: &'static [FrameFormat] = &[ - $( - $(FrameFormat::$format,)* + pub const $classifier: &'static [FrameFormat] = &[ + $($($(FrameFormat::$format,)*)*)* + ]; + $( + + pub const [<$classifier _ $sub_classifier>]: &'static [FrameFormat] = &[ + $($(FrameFormat::$format,)*)* + ]; + $( + pub const [<$classifier _ $sub_classifier _ $group_name>]: &'static [FrameFormat] = &[ + $(FrameFormat::$format,)* + ]; + )* + )* )* - ]; + pub const ALL: &'static [FrameFormat] = &[ + $($( + $($(FrameFormat::$format,)*)* + )*)* + ]; + } } }; } define_frame_format_with_groups! { - COMPRESSED => [ - H265, - HEVC, - H264, - AVC1, - H263, - AV1, - MPEG_1, - MPEG_2, - MPEG_4, - MJPEG, - XviD, - VP8, - VP9, + COMPRESSED [ + MPEG [ + H => [ + H265, + HEVC, + H264, + H263, + ], + MPEG4 => [ + AVC1, + MPEG_4, + XviD, + ], + MPEG => [ + MPEG_1, + MPEG_2, + ], + ], + IMAGE [ + MJPEG => [ + MJPEG, + ] + ], + OPEN [ + AOM => [ + AV1 + ], + WEB => [ + VP8, + VP9 + ], + ] ], - YCBCR_PACKED_444 => [ - Ayuv_32, - ], - YCBCR_PLANAR_444 => [], - YCBCR_SEMI_PLANAR_444 => [ - NV24, - NV42, - ], - YCBCR_PACKED_422 => [ - Yuyv_4_2_2, - Uyvy_4_2_2, - Vyuy_4_2_2, - Yvyu_4_2_2, - Y210, - Y216, - ], - YCBCR_PLANAR_422 => [], - YCBCR_SEMI_PLANAR_422 => [ - NV16, - NV61, - ], - YCBCR_PACKED_420 => [], - YCBCR_PLANAR_420 => [ - Yuv_4_2_0, - Yvu_4_2_0, - ], - YCBCR_SEMI_PLANAR_420 => [ - NV12, - NV21, - P010, - P012, - - ], - YCBCR_PACKED_411 => [ - Y41Packed - ], - YCBCR_PLANAR_411 => [ - Y411Planar - ], - YCBCR_SEMI_PLANAR_411 => [ - NV11, + YCBCR [ + PACKED [ + 444 => [ + Ayuv_32, + ], + 422 => [ + Yuyv_4_2_2, + Uyvy_4_2_2, + Vyuy_4_2_2, + Yvyu_4_2_2, + ], + 420 => [], + 411 => [ + ] + ], + PLANAR [ + 444 => [], + 422 => [], + 420 => [ + Yuv_4_2_0, + ], + 411 => [ + ] + ], + SEMI [ + 444 => [ + NV24, + NV42, + ], + 422 => [ + NV16, + NV61, + ], + 420 => [ + NV12, + NV21, + P010, + P012, + ], + 411 => [ + ] + ], ], - LUMA => [ - Luma_8, - Luma_10, - Luma_12, - Luma_14, - Luma_16, - Depth_16, + BRIGHTNESS [ + LUMA [ + SMALL => [ + Luma_8, + ], + LARGE => [ + Luma_10, + Luma_12, + Luma_14, + Luma_16, + ], + ], + DEPTH [ + SMALL => [], + LARGE => [Depth_16], + ] ], - RAW_RGB => [ - Rgb_3_3_2, - Rgb_5_6_5, - Rgb_5_5_5, - Rgb_8_8_8, - Argb_8_8_8_8, - Rgba_8_8_8_8, - ], - - RAW_BGR => [ - Bgr_3_3_2, - Bgr_5_6_5, - Bgr_5_5_5, - Bgr_8_8_8, - Abgr_8_8_8_8, - Bgra_8_8_8_8, + RAW [ + RGB [ + NO_ALPHA => [ + Rgb_3_3_2, + Rgb_5_6_5, + Rgb_5_5_5, + Rgb_8_8_8, + ], + WITH_ALPHA => [ + Argb_8_8_8_8, + Rgba_8_8_8_8, + ] + ], + BGR [ + NO_ALPHA => [ + Bgr_3_3_2, + Bgr_5_6_5, + Bgr_5_5_5, + Bgr_8_8_8, + ], + WITH_ALPHA => [ + Abgr_8_8_8_8, + Bgra_8_8_8_8, + ] + ] ] } -// define_frame_format_groups! { -// ALL => [ -// // Compressed Formats -// H265, -// H264, -// Avc1, -// H263, -// Av1, -// Mpeg1, -// Mpeg2, -// Mpeg4, -// MJpeg, -// XVid, -// VP8, -// VP9, -// -// // YCbCr Formats -// -// // 8 bit per pixel, 4:4:4 -// Ayuv444, -// -// // -> 4:2:2 -// Yuyv422, // AKA YUY2 -// Uyvy422, // UYUV -// Yvyu422, -// Yv12, -// -// // 4:2:0 -// Nv12, -// Nv21, -// I420, -// -// // 16:1:1 -// Yvu9, -// -// // Grayscale Formats -// Luma8, -// Luma16, -// -// // Depth -// Depth16, -// -// // RGB Formats -// Rgb332, -// Rgb888, -// -// Bgr888, -// BgrA8888, -// -// RgbA8888, -// ARgb8888, -// -// // Bayer Formats -// Bayer8, -// Bayer16, -// ], -// COMPRESSED => [ -// H265, -// H264, -// Avc1, -// H263, -// Av1, -// Mpeg1, -// Mpeg2, -// Mpeg4, -// MJpeg, -// XVid, -// VP8, -// VP9, -// ], -// YCBCR => [ -// Ayuv444, -// -// // -> 4:2:2 -// Yuyv422, // AKA YUY2 -// Uyvy422, // UYUV -// Yvyu422, -// Yv12, -// -// // 4:2:0 -// Nv12, -// Nv21, -// I420, -// -// // 16:1:1 -// Yvu9, -// ], -// YCBCR_PACKED => [ -// Ayuv444, -// -// // -> 4:2:2 -// Yuyv422, // AKA YUY2 -// Uyvy422, // UYUV -// Yvyu422, -// -// // 4:2:0 -// Nv12, -// Nv21, -// I420, -// -// ], -// YCBCR_PLANAR => [ Yvu9, -// Yv12, -// ], -// LUMA => [ -// Luma8, Luma16 -// ], -// RGB => [ -// Rgb332, RgbA8888 -// ], -// COLOR_FORMATS => [ -// H265, H264, H263, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid, -// VP8, VP9, Yuyv422, Uyvy422, Nv12, Nv21, Yv12, Rgb332, RgbA8888 -// ], -// GRAYSCALE => [ -// Luma8, Luma16, Depth16, -// ], -// RAW => [ -// // RGB Formats -// Rgb332, -// Rgb888, -// -// Bgr888, -// BgrA8888, -// -// RgbA8888, -// ARgb8888,], -// BAYER => [ -// // Bayer Formats -// Bayer8, -// Bayer16,], -// } - impl Display for FrameFormat { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{self:?}") @@ -346,6 +212,7 @@ impl Display for FrameFormat { } #[derive(Copy, Clone, Debug, Hash, PartialOrd, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum CustomFrameFormat { UUID(u128), FourCC([char; 4]), diff --git a/nokhwa-core/src/image.rs b/nokhwa-core/src/image.rs index c737bdf..2802448 100644 --- a/nokhwa-core/src/image.rs +++ b/nokhwa-core/src/image.rs @@ -1,8 +1,8 @@ +use bytemuck::Pod; use image::{ImageBuffer, Pixel, Primitive}; +use num_traits::{NumCast, PrimInt}; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; -use bytemuck::Pod; -use num_traits::{NumCast, PrimInt}; #[derive(Debug)] pub struct DecodedImage @@ -15,16 +15,14 @@ where pub metadata: Meta, } -impl DecodedImage where +impl DecodedImage +where Px: Pixel, ::Subpixel: NonFloatScalarWidth, - Meta: Debug { - pub fn new(buffer: ImageBuffer>, - metadata: Meta) -> Self { - Self { - buffer, - metadata, - } + Meta: Debug, +{ + pub fn new(buffer: ImageBuffer>, metadata: Meta) -> Self { + Self { buffer, metadata } } } @@ -32,7 +30,7 @@ impl Deref for DecodedImage where Px: Pixel, ::Subpixel: NonFloatScalarWidth, - Meta: Debug + Meta: Debug, { type Target = ImageBuffer>; @@ -44,7 +42,8 @@ where impl DerefMut for DecodedImage where Px: Pixel, - ::Subpixel: NonFloatScalarWidth,Meta: Debug + ::Subpixel: NonFloatScalarWidth, + Meta: Debug, { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.buffer @@ -52,27 +51,27 @@ where } // not for safe work ;p -// TODO: add more custom integer sizes, or break our dependence on image entirely and +// TODO: add more custom integer sizes, or break our dependence on image entirely and // create our own imagebuffer pub trait NonFloatScalarWidth: Debug + Primitive + PrimInt + NumCast + Pod { - const WIDTH: u32; + const WIDTH_BYTES: u32; } macro_rules! impl_nfsw { - ( $( [ ( $( $things:ty ),+ ) : $size:literal ] ),* $(,)? ) => { + ( $( [ ( $( $things:ty ),* ) : $size:literal ] ),* $(,)? ) => { $( $( impl NonFloatScalarWidth for $things { - const WIDTH: u32 = $size; + const WIDTH_BYTES: u32 = $size; } - )+ + )* )* } } impl_nfsw! { - [ (u8, i8) : 8 ], - [ (u16, i16) : 16 ], - [ (u32, i32) : 32 ], - [ (u64, i64) : 64 ], + [ (u8, i8) : 1 ], + [ (u16, i16) : 2 ], + [ (u32, i32) : 4 ], + [ (u64, i64) : 8 ], } diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index 415622d..23ed70f 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -35,3 +35,4 @@ pub mod stream; pub mod traits; pub mod types; pub mod utils; +mod metadata; diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index d379967..84ca4ce 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -31,12 +31,16 @@ pub trait PlatformTrait { fn open(&mut self, index: CameraIndex) -> NokhwaResult; - fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult> where ::Camera: 'static { + 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; @@ -47,7 +51,12 @@ pub trait AsyncPlatformTrait: PlatformTrait { 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/ranges.rs b/nokhwa-core/src/ranges.rs index 8c46700..a004518 100644 --- a/nokhwa-core/src/ranges.rs +++ b/nokhwa-core/src/ranges.rs @@ -148,16 +148,11 @@ where fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char { if inclusive { - if upper { - ']' - } else { - '[' - } + if upper { ']' } else { '[' } } else if upper { - ')' - } else { - '(' - + ')' + } else { + '(' } } @@ -183,9 +178,9 @@ macro_rules! impl_num { ($($n:ty)*) => ($( impl RangeItem for $n { const ZERO: $n = 0; - + const MIN: $n = <$n>::MIN; - + const MAX: $n = <$n>::MAX; } )*) diff --git a/nokhwa-core/src/stream.rs b/nokhwa-core/src/stream.rs index ddf6d53..4ab5f07 100644 --- a/nokhwa-core/src/stream.rs +++ b/nokhwa-core/src/stream.rs @@ -59,9 +59,9 @@ pub struct StreamConfiguration { /// Possible events to receive from an active stream. #[derive(Clone, Debug, PartialEq)] -pub enum Event { +pub enum Event<'a> { /// A new frame. - NewFrame(FrameBuffer), + NewFrame(FrameBuffer<'a>), /// Camera Format Changed. /// /// This will usually require the reset of a buffer, or be followed by a [`Event::Terminated`], @@ -97,17 +97,18 @@ pub enum Event { /// /// You may also close the stream from the handle side using #[derive(Debug)] -pub struct StreamHandle { - frame: Receiver, +pub struct StreamHandle<'a> { + frame: Receiver>, control: Arc>, configuration: StreamConfiguration, format: Cell, } -impl StreamHandle { +impl<'a> StreamHandle<'a> { /// You shouldn't be here. - #[must_use] pub fn new( - recv: Receiver, + #[must_use] + pub fn new( + recv: Receiver>, control: Arc>, configuration: StreamConfiguration, format: CameraFormat, @@ -128,19 +129,19 @@ impl StreamHandle { self.format.get() } - pub fn next_event(&self) -> Result { + 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, - }), + .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 { @@ -150,7 +151,7 @@ impl StreamHandle { Ok(event) } - pub fn next_frame(&self) -> Result { + pub fn next_frame(&self) -> Result, NokhwaError> { loop { let event = self.next_event()?; match event { @@ -159,7 +160,11 @@ impl StreamHandle { 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::Other(why) => { + if self.configuration.on_other == ControlFlowOnOther::Break { + return Err(NokhwaError::ReadFrameError(why)); + } + } Event::Error(e) => return Err(NokhwaError::ReadFrameError(e.to_string())), _ => {} } @@ -167,7 +172,7 @@ impl StreamHandle { } #[cfg(feature = "async")] - pub async fn poll_event(&self) -> Result { + pub async fn poll_event(&self) -> Result, NokhwaError> { Ok(self.frame.recv_async().await.map_or_else( |_| Event::Closed, |e| { @@ -181,7 +186,7 @@ impl StreamHandle { // TODO: a smarter implementation? maybe? #[cfg(feature = "async")] - pub async fn poll_next_frame(&self) -> Result { + pub async fn poll_next_frame(&self) -> Result, NokhwaError> { loop { let event = self.poll_event().await?; match event { @@ -190,17 +195,18 @@ impl StreamHandle { let _ = self.control.try_send(()); return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string())); } - Event::Other(why) => match self.configuration.on_other { - ControlFlowOnOther::Continue => continue, - ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why)), - }, + Event::Other(why) => { + if let ControlFlowOnOther::Break = self.configuration.on_other { + return Err(NokhwaError::ReadFrameError(why)); + } + } _ => {} } } } } -impl Drop for StreamHandle { +impl Drop for StreamHandle<'_> { fn drop(&mut self) { let _ = self.control.try_send(()); } diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 0d8b698..f07d739 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -99,18 +99,18 @@ impl TryFrom for usize { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] pub struct Resolution { - width_x: u32, - height_y: u32, + width: u32, + height: u32, } impl Resolution { /// Create a new resolution from 2 image size coordinates. #[must_use] // TODO: make this height and width. - pub const fn new(x: u32, y: u32) -> Self { + pub const fn new(width: u32, height: u32) -> Self { Resolution { - width_x: x, - height_y: y, + width, + height, } } @@ -118,33 +118,33 @@ impl Resolution { #[must_use] #[inline] pub fn width(self) -> u32 { - self.width_x + self.width } /// Get the height of Resolution #[must_use] #[inline] pub fn height(self) -> u32 { - self.height_y + self.height } /// Get the x (width) of Resolution #[must_use] #[inline] pub fn x(self) -> u32 { - self.width_x + self.width } /// Get the y (height) of Resolution #[must_use] #[inline] pub fn y(self) -> u32 { - self.height_y + self.height } #[must_use] pub fn aspect_ratio(&self) -> f64 { - f64::from(self.width_x) / f64::from(self.height_y) + f64::from(self.width) / f64::from(self.height) } } @@ -232,31 +232,37 @@ pub struct FrameRate { } impl FrameRate { - #[must_use] pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self { + #[must_use] + pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self { Self { rational: Rational32::new_raw(numerator, denominator.get()), } } - #[must_use] pub const fn from_fps(fps: i32) -> Self { + #[must_use] + pub const fn from_fps(fps: i32) -> Self { Self { rational: Rational32::new_raw(fps, 1), } } - #[must_use] pub fn numerator(&self) -> i32 { + #[must_use] + pub fn numerator(&self) -> i32 { *self.rational.numer() } - #[must_use] pub fn denominator(&self) -> i32 { + #[must_use] + pub fn denominator(&self) -> i32 { *self.rational.denom() } - #[must_use] pub fn as_raw(&self) -> &Rational32 { + #[must_use] + pub fn as_raw(&self) -> &Rational32 { &self.rational } - #[must_use] pub fn approximate_float(&self) -> Option { + #[must_use] + pub fn approximate_float(&self) -> Option { let numerator_float = f32::from_i32(self.numerator())?; let denominator_float = f32::from_i32(self.denominator())?; @@ -346,8 +352,8 @@ impl CameraFormat { pub const fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: FrameRate) -> Self { CameraFormat { resolution: Resolution { - width_x: res_x, - height_y: res_y, + width: res_x, + height: res_y, }, format, frame_rate: fps, @@ -356,8 +362,8 @@ impl CameraFormat { /// Get the resolution of the current [`CameraFormat`] #[must_use] - pub fn resolution(&self) -> &Resolution { - &self.resolution + pub fn resolution(&self) -> Resolution { + self.resolution } /// Get the width of the resolution of the current [`CameraFormat`] @@ -379,8 +385,8 @@ impl CameraFormat { /// Get the frame rate of the current [`CameraFormat`] #[must_use] - pub fn frame_rate(&self) -> &FrameRate { - &self.frame_rate + pub fn frame_rate(&self) -> FrameRate { + self.frame_rate } /// Set the [`CameraFormat`]'s frame rate. @@ -390,8 +396,8 @@ impl CameraFormat { /// Get the [`CameraFormat`]'s format. #[must_use] - pub fn format(&self) -> &FrameFormat { - &self.format + pub fn format(&self) -> FrameFormat { + self.format } /// Set the [`CameraFormat`]'s format. diff --git a/nokhwa-decoders/Cargo.toml b/nokhwa-decoders/Cargo.toml index f1551a1..efd74f7 100644 --- a/nokhwa-decoders/Cargo.toml +++ b/nokhwa-decoders/Cargo.toml @@ -7,9 +7,9 @@ edition = "2024" ffmpeg = ["ffmpeg-the-third"] av1 = ["rav1e"] yuyv = ["dcv-color-primitives", "yuv"] -mjpeg = ["zune-jpeg"] -wasm = [] -mega = ["ffmpeg", "av1", "yuyv", "mjpeg"] +mjpeg = ["zune-jpeg", "zune-core"] +browser = [] +general = ["ffmpeg", "av1", "yuyv", "mjpeg"] static = ["ffmpeg-the-third/static"] async = [] @@ -37,5 +37,9 @@ version = "0.7" optional = true [dependencies.zune-jpeg] +version = "0.5.0-rc7" +optional = true + +[dependencies.zune-core] version = "0.5.0-rc2" optional = true diff --git a/nokhwa-decoders/src/ffmpeg.rs b/nokhwa-decoders/src/ffmpeg.rs index de46dc5..fc672bd 100644 --- a/nokhwa-decoders/src/ffmpeg.rs +++ b/nokhwa-decoders/src/ffmpeg.rs @@ -1,10 +1,17 @@ -use core::mem::transmute; use bytemuck::try_cast_slice_mut; +use core::mem::transmute; use ffmpeg_the_third::codec::{Context, Id, Parameters}; use ffmpeg_the_third::decoder::Video; -use ffmpeg_the_third::ffi::{av_frame_alloc, av_frame_move_ref, av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context, avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext, sws_scale_frame, AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace, AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, AVPacketSideData, AVPixelFormat, AVRational, SwsContext}; -use ffmpeg_the_third::{decoder, packet::Packet, Frame}; +use ffmpeg_the_third::ffi::{ + AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace, + AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, + AVPixelFormat, AVRational, SwsContext, av_frame_alloc, av_frame_move_ref, + av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context, + avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext, + sws_scale_frame, +}; use ffmpeg_the_third::packet::{Borrow, Ref}; +use ffmpeg_the_third::{Frame, decoder, packet::Packet}; use nokhwa_core::codec::Codec; use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive}; use nokhwa_core::error::NokhwaError; @@ -19,40 +26,38 @@ pub struct FfmpegDecoder { } impl FfmpegDecoder { - pub fn new(config: ::Config) -> Result { + pub fn new( + config: ::Config, + ) -> Result { let codec = FfmpegCodec::new(config)?; - Ok( - Self { - codec, - sws: None, - } - ) + Ok(Self { codec, sws: None }) } pub fn with_format(format: &CameraFormat) -> Result { let config = FfmpegDecoderConfig::with_camera_format(format); let codec = FfmpegCodec::new(config)?; - Ok( - Self { - codec, - sws: None, - } - ) + Ok(Self { codec, sws: None }) } - pub fn receive_decoded_frame(&mut self, to_decode: FrameBuffer) -> Result<(Frame, ::OutputMeta) , NokhwaError> { + pub fn receive_decoded_frame( + &mut self, + to_decode: FrameBuffer, + ) -> Result<(Frame, ::OutputMeta), NokhwaError> { let new_pkt = Packet::borrow(to_decode.buffer()); self.codec.send_item(new_pkt.into())?; let mut new_frame = unsafe { Frame::empty() }; - let metadata = self.codec.receive_decoded_item(&mut new_frame).map_err(|why| NokhwaError::Decoder(why.to_string()))?; + let metadata = self + .codec + .receive_decoded_item(&mut new_frame) + .map_err(|why| NokhwaError::Decoder(why.to_string()))?; Ok((new_frame, metadata)) } - } impl Decoder for FfmpegDecoder { type Config = ::Config; type OutputMeta = ::WrittenMeta; + type DestinationFormatHint = AVPixelFormat; fn config(&self) -> &Self::Config { self.codec.config() @@ -63,37 +68,77 @@ impl Decoder for FfmpegDecoder { Ok(()) } - fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>) -> Result { + fn decode_to_buffer( + &mut self, + to_decode: FrameBuffer, + mut buffer: impl AsMut<[u8]>, + _destination_format: Option + ) -> Result { // TODO: add an extra zippy happy path for rgb/bgr/luma let (frame, metadata) = self.receive_decoded_frame(to_decode)?; - let av_frame_data = unsafe { frame.as_ref().ok_or(NokhwaError::Decoder("No Frame Data from Decoder".to_string()))? }; + let av_frame_data = unsafe { + frame.as_ref().ok_or(NokhwaError::Decoder( + "No Frame Data from Decoder".to_string(), + ))? + }; let buffer = buffer.as_mut(); let result = unsafe { - av_image_copy_to_buffer(buffer.as_mut().as_mut_ptr(), buffer.as_mut().len() as i32, av_frame_data.data.as_ptr() as *const *const u8, av_frame_data.linesize.as_ptr(), metadata.pixel_format, self.config().resolution.width() as i32, self.config().resolution.height() as i32, 1) + av_image_copy_to_buffer( + buffer.as_mut().as_mut_ptr(), + buffer.as_mut().len() as i32, + av_frame_data.data.as_ptr() as *const *const u8, + av_frame_data.linesize.as_ptr(), + metadata.pixel_format, + self.config().resolution.width() as i32, + self.config().resolution.height() as i32, + 1, + ) }; if result.is_negative() { - return Err(NokhwaError::Decoder(format!("Error Code {}", result))) + return Err(NokhwaError::Decoder(format!("Error Code {result}"))); } Ok(metadata) } - fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[P::Subpixel]>) -> Result where

::Subpixel: NonFloatScalarWidth { - let destination_format = pixel_to_destination_px_fmt::

(::WIDTH).ok_or(NokhwaError::Decoder("Unsupported Pixel Type".to_string()))?; + fn decode_to_pixel_buffer( + &mut self, + to_decode: FrameBuffer, + mut buffer: impl AsMut<[P::Subpixel]>, + ) -> Result + where +

::Subpixel: NonFloatScalarWidth, + { + let destination_format = + pixel_to_destination_px_fmt::

() + .ok_or(NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()))?; let buffer = buffer.as_mut(); - let estimated_size = self.codec.preferred_buffer_min_size(&None)?.ok_or(NokhwaError::Decoder("failed to estimate decoder buffer.length".to_string()))?; + let estimated_size = + self.codec + .preferred_buffer_min_size(&None)? + .ok_or(NokhwaError::DecoderInvalidBuffer( + "failed to estimate decoder buffer.length".to_string(), + ))?; if buffer.len() < estimated_size { - return Err(NokhwaError::Decoder("buffer too small!".to_string())) + return Err(NokhwaError::DecoderInvalidBuffer("buffer too small!".to_string())); } - let (mut frame, decoded_meta) = self.receive_decoded_frame(to_decode)?; let source_format = decoded_meta.pixel_format; - - let cast_slice = try_cast_slice_mut::(buffer).map_err(|why| NokhwaError::Decoder(why.to_string()))?; + + let cast_slice = try_cast_slice_mut::(buffer) + .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?; let receiving_buffer = unsafe { let av_frame = av_frame_alloc(); - let result = av_image_fill_arrays((*av_frame).data.as_mut_ptr(), (*av_frame).linesize.as_mut_ptr(), cast_slice.as_ptr(), destination_format, self.codec.config.resolution.width() as i32, self.codec.config.resolution.height() as i32, 1); + let result = av_image_fill_arrays( + (*av_frame).data.as_mut_ptr(), + (*av_frame).linesize.as_mut_ptr(), + cast_slice.as_ptr(), + destination_format, + self.codec.config.resolution.width() as i32, + self.codec.config.resolution.height() as i32, + 1, + ); if result < 0 { return Err(NokhwaError::Decoder("Failed to fill avimage".to_string())); } @@ -103,7 +148,11 @@ impl Decoder for FfmpegDecoder { if source_format != destination_format { let sws_scaler = match &mut self.sws { None => { - let context = create_sws_context(source_format, destination_format, self.codec.config.resolution)?; + let context = create_sws_context( + source_format, + destination_format, + self.codec.config.resolution, + )?; self.sws = Some(Sws { sws: context, source_pixel_format: source_format, @@ -111,79 +160,135 @@ impl Decoder for FfmpegDecoder { }); self.sws.as_mut().unwrap() } - Some(v) => { - if v.source_pixel_format != source_format || v.dest_pixel_format != destination_format { + Some(v) => { + if v.source_pixel_format != source_format + || v.dest_pixel_format != destination_format + { v.source_pixel_format = source_format; v.dest_pixel_format = destination_format; - v.sws = create_sws_context(source_format, destination_format, self.codec.config.resolution)?; + v.sws = create_sws_context( + source_format, + destination_format, + self.codec.config.resolution, + )?; } v } }; - let scaled = unsafe {sws_scale_frame(sws_scaler.sws, receiving_buffer, frame.as_mut_ptr())}; + let scaled = + unsafe { sws_scale_frame(sws_scaler.sws, receiving_buffer, frame.as_mut_ptr()) }; if scaled < 0 { Err(NokhwaError::Decoder("Failed to scale sws".to_string())) } else { Ok(decoded_meta) } - } else { - unsafe { - av_frame_move_ref(receiving_buffer, frame.as_mut_ptr()) - }; + unsafe { av_frame_move_ref(receiving_buffer, frame.as_mut_ptr()) }; Ok(decoded_meta) } - } - fn decode(&mut self, to_decode: FrameBuffer) -> Result, NokhwaError> where

::Subpixel: NonFloatScalarWidth { - let min_size = P::CHANNEL_COUNT as usize * (::WIDTH as usize / 8_usize) * self.codec.config.resolution.height() as usize * self.codec.config.resolution.width() as usize; + fn decode( + &mut self, + to_decode: FrameBuffer, + ) -> Result, NokhwaError> + where +

::Subpixel: NonFloatScalarWidth, + { + let min_size = self.output_decoder_min_size_pixel::

(self.config().resolution); let mut buffer: Vec = vec![::DEFAULT_MIN_VALUE; min_size]; - let meta = self.decode_to_buffer(to_decode, try_cast_slice_mut(&mut buffer).map_err(|why| NokhwaError::Decoder(why.to_string()))?)?; - Ok(DecodedImage::new(ImageBuffer::from_vec(self.codec.config.resolution.width(), self.codec.config.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to create Image Buffer".to_string()))?, meta)) + let meta = self.decode_to_buffer(to_decode, try_cast_slice_mut(&mut buffer).map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?, None)?; + Ok(DecodedImage::new( + ImageBuffer::from_vec( + self.codec.config.resolution.width(), + self.codec.config.resolution.height(), + buffer, + ) + .ok_or(NokhwaError::Decoder( + "Failed to create Image Buffer".to_string(), + ))?, + meta, + )) } - } -fn create_sws_context(src_format: AVPixelFormat, dest: AVPixelFormat, resolution: Resolution) -> Result<*mut SwsContext, NokhwaError> { + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + let size = + unsafe { av_image_get_buffer_size(destination_format, resolution.width() as i32, resolution.height() as i32, 1) }; + size as usize + } + + fn buffer_takes_destination_hint(&self) -> bool { + false + } +} + +fn create_sws_context( + src_format: AVPixelFormat, + dest: AVPixelFormat, + resolution: Resolution, +) -> Result<*mut SwsContext, NokhwaError> { let param = 0_f64; let new_sws = unsafe { - sws_getContext(resolution.width() as i32, resolution.height() as i32, src_format, resolution.width() as i32, resolution.height() as i32, dest, 0, core::ptr::null_mut(), core::ptr::null_mut(), ¶m) + sws_getContext( + resolution.width() as i32, + resolution.height() as i32, + src_format, + resolution.width() as i32, + resolution.height() as i32, + dest, + 0, + core::ptr::null_mut(), + core::ptr::null_mut(), + ¶m, + ) }; Ok(new_sws) } -fn pixel_to_destination_px_fmt(width: u32) -> Option where

::Subpixel: NonFloatScalarWidth { +fn pixel_to_destination_px_fmt() -> Option +where +

::Subpixel: NonFloatScalarWidth, +{ match P::COLOR_MODEL { - "RGB" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_RGB24), + "RGB" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_RGB24), _ => None, - } + }, - "RGBA" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_RGBA), - 16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_RGBA64LE, AVPixelFormat::AV_PIX_FMT_RGBA64BE)), + "RGBA" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_RGBA), + 2 => Some(switch_endian( + AVPixelFormat::AV_PIX_FMT_RGBA64LE, + AVPixelFormat::AV_PIX_FMT_RGBA64BE, + )), _ => None, - } - "BGR" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_BGR24), + }, + "BGR" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_BGR24), _ => None, - } + }, - "BGRA" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_BGRA), - 16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_BGRA64LE, AVPixelFormat::AV_PIX_FMT_BGRA64BE)), + "BGRA" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_BGRA), + 2 => Some(switch_endian( + AVPixelFormat::AV_PIX_FMT_BGRA64LE, + AVPixelFormat::AV_PIX_FMT_BGRA64BE, + )), _ => None, - } - "Y" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8), - 16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_GRAY16LE, AVPixelFormat::AV_PIX_FMT_GRAY16BE)), + }, + "Y" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8), + 2 => Some(switch_endian( + AVPixelFormat::AV_PIX_FMT_GRAY16LE, + AVPixelFormat::AV_PIX_FMT_GRAY16BE, + )), _ => None, - } - "YA" => match width { - 8 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8A), + }, + "YA" => match <

::Subpixel>::WIDTH_BYTES { + 1 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8A), _ => None, - } + }, _ => None, } } @@ -206,7 +311,6 @@ impl Drop for Sws { } } - #[derive(Clone, Debug)] pub struct FfmpegFrameMetadata { pub color_range: AVColorRange, @@ -216,7 +320,7 @@ pub struct FfmpegFrameMetadata { pub enum PacketOrRef<'a> { Packet(Packet), - Ref(Borrow<'a>) + Ref(Borrow<'a>), } impl Ref for PacketOrRef<'_> { @@ -249,20 +353,33 @@ pub struct FfmpegCodec { impl FfmpegCodec { fn new(config: ::Config) -> Result { - let id = convert_format_to_codec_id(&config.frame_format).ok_or(NokhwaError::Decoder("Could not find Format".to_string()))?; + let id = convert_format_to_codec_id(&config.frame_format) + .ok_or(NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format))?; - let codec = decoder::find(id).ok_or(NokhwaError::Decoder("Failed to find codec".to_string()))?; + let codec = + decoder::find(id).ok_or(NokhwaError::DecoderInitializationError("Failed to find codec".to_string()))?; let context = unsafe { let ptr = ffmpeg_the_third::ffi::avcodec_alloc_context3(codec.as_ptr()); if ptr.is_null() { - return Err(NokhwaError::Decoder("ffmpeg returned a null context".to_string())) + return Err(NokhwaError::DecoderInitializationError( + "ffmpeg returned a null context".to_string(), + )); } Context::wrap(ptr, None) }; - let mut video = context.decoder().video().map_err(|why| NokhwaError::Decoder(why.to_string()))?; - video.set_parameters(unsafe { Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::Decoder("Failed to convert parameters".to_string()))? }).map_err(|why| NokhwaError::Decoder(why.to_string()))?; + let mut video = context + .decoder() + .video() + .map_err(|why| NokhwaError::Decoder(why.to_string()))?; + video + .set_parameters(unsafe { + Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::DecoderInitializationError( + "Failed to convert parameters".to_string(), + ))? + }) + .map_err(|why| NokhwaError::Decoder(why.to_string()))?; Ok(Self { decoder: video, @@ -274,32 +391,36 @@ impl FfmpegCodec { } impl Codec for FfmpegCodec { - type Config = FfmpegDecoderConfig; type Input<'a> = PacketOrRef<'a>; - type Output = Frame; + type Output<'a> = Frame; type WrittenMeta = FfmpegFrameMetadata; fn allowed_formats(&self) -> Result<&[FrameFormat], NokhwaError> { if self.deinitialized { - return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string())) + return Err(NokhwaError::DecoderAlreadyDeinitialized); } - + Ok(FrameFormat::ALL) } - fn config(&self) -> &Self::Config { &self.config } fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> { if self.deinitialized { - return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string())) + return Err(NokhwaError::DecoderAlreadyDeinitialized); } dealloc_av_params(&mut self.temp_cfg); let mut temp_config = config.as_avcodec_params()?; - self.decoder.set_parameters(unsafe { Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::Decoder("Failed to convert parameters".to_string()))? }).map_err(|why| NokhwaError::Decoder(why.to_string()))?; + self.decoder + .set_parameters(unsafe { + Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::DecoderInvalidConfiguration( + "Failed to convert parameters".to_string(), + ))? + }) + .map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?; self.config = config; self.temp_cfg = Some(&mut temp_config); Ok(()) @@ -307,19 +428,30 @@ impl Codec for FfmpegCodec { fn send_item(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError> { if self.deinitialized { - return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string())) + return Err(NokhwaError::DecoderAlreadyDeinitialized); } - self.decoder.send_packet(&input).map_err(|why| NokhwaError::Decoder(why.to_string())) + self.decoder + .send_packet(&input) + .map_err(|why| NokhwaError::Decoder(why.to_string())) } - fn receive_decoded_item(&mut self, writing_output: &mut Self::Output) -> Result { + fn receive_decoded_item( + &mut self, + writing_output: &mut Self::Output<'_>, + ) -> Result { if self.deinitialized { - return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string())) + return Err(NokhwaError::DecoderAlreadyDeinitialized); } - self.decoder.receive_frame(writing_output).map_err(|why| NokhwaError::Decoder(why.to_string()))?; + self.decoder + .receive_frame(writing_output) + .map_err(|why| NokhwaError::Decoder(why.to_string()))?; let avframe = match unsafe { writing_output.as_ref() } { Some(r) => r, - None => return Err(NokhwaError::Decoder("failed to get refrenece to decoded frame".to_string())) + None => { + return Err(NokhwaError::Decoder( + "failed to get refrenece to decoded frame".to_string(), + )); + } }; let meta = FfmpegFrameMetadata { color_range: avframe.color_range, @@ -329,26 +461,32 @@ impl Codec for FfmpegCodec { Ok(meta) } - fn preferred_buffer_min_size(&mut self, camera_format: &Option) -> Result, NokhwaError> { + fn preferred_buffer_min_size( + &mut self, + camera_format: &Option, + ) -> Result, NokhwaError> { let (width, height, pixel_format) = match camera_format { - Some(fmt) => {( + Some(fmt) => ( fmt.width(), fmt.height(), - convert_frame_format_to_pixfmt(fmt.format())) - } - None => { -( self.config().resolution.width(), self.config.resolution.height(), convert_frame_format_to_pixfmt(&self.config().frame_format) -) } + convert_frame_format_to_pixfmt(&fmt.format()), + ), + None => ( + self.config().resolution.width(), + self.config.resolution.height(), + convert_frame_format_to_pixfmt(&self.config().frame_format), + ), }; - let size = unsafe { av_image_get_buffer_size(pixel_format, width as i32, height as i32, 1) }; + let size = + unsafe { av_image_get_buffer_size(pixel_format, width as i32, height as i32, 1) }; Ok(Some(size as usize)) } fn deinitialize(&mut self) -> Result<(), NokhwaError> { dealloc_av_params(&mut self.temp_cfg); if self.deinitialized { - return Ok(()) + return Ok(()); } self.deinitialized = true; @@ -396,9 +534,7 @@ fn convert_format_to_codec_id(frame_format: &FrameFormat) -> Option { FrameFormat::Custom(c) => { if let CustomFrameFormat::U32(c_id) = c { // SAFETY: /shrug - let av_codec_id: AVCodecID = unsafe { - transmute(*c_id) - }; + let av_codec_id: AVCodecID = unsafe { transmute(*c_id) }; Some(av_codec_id.into()) } else { None @@ -416,73 +552,86 @@ fn convert_frame_format_to_pixfmt(frame_format: &FrameFormat) -> AVPixelFormat { FrameFormat::Yuyv_4_2_2 => AVPixelFormat::AV_PIX_FMT_YUYV422, FrameFormat::Uyvy_4_2_2 => AVPixelFormat::AV_PIX_FMT_UYVY422, FrameFormat::Yvyu_4_2_2 => AVPixelFormat::AV_PIX_FMT_YVYU422, - FrameFormat::Y210 => { - if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_Y210LE } else { - AVPixelFormat::AV_PIX_FMT_Y210BE } - }, - FrameFormat::Y216 => { - if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_YUV422P16LE } else { - AVPixelFormat::AV_PIX_FMT_YUV422P16BE } - } FrameFormat::NV16 => AVPixelFormat::AV_PIX_FMT_NV16, FrameFormat::Yuv_4_2_0 => AVPixelFormat::AV_PIX_FMT_YUV420P, FrameFormat::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, FrameFormat::NV21 => AVPixelFormat::AV_PIX_FMT_NV21, FrameFormat::P010 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_P010LE } else { - AVPixelFormat::AV_PIX_FMT_P010BE } - }, + AVPixelFormat::AV_PIX_FMT_P010LE + } else { + AVPixelFormat::AV_PIX_FMT_P010BE + } + } FrameFormat::P012 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_P012LE } else { - AVPixelFormat::AV_PIX_FMT_P012BE } - }, - FrameFormat::Y411Planar => AVPixelFormat::AV_PIX_FMT_YUV411P, + AVPixelFormat::AV_PIX_FMT_P012LE + } else { + AVPixelFormat::AV_PIX_FMT_P012BE + } + } FrameFormat::Luma_8 => AVPixelFormat::AV_PIX_FMT_GRAY8, FrameFormat::Luma_10 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_GRAY10LE } else { - AVPixelFormat::AV_PIX_FMT_GRAY10BE } - }, + AVPixelFormat::AV_PIX_FMT_GRAY10LE + } else { + AVPixelFormat::AV_PIX_FMT_GRAY10BE + } + } FrameFormat::Luma_12 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_GRAY12LE } else { - AVPixelFormat::AV_PIX_FMT_GRAY12BE } - }, + AVPixelFormat::AV_PIX_FMT_GRAY12LE + } else { + AVPixelFormat::AV_PIX_FMT_GRAY12BE + } + } FrameFormat::Luma_14 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_GRAY14LE } else { - AVPixelFormat::AV_PIX_FMT_GRAY14BE } - }, + AVPixelFormat::AV_PIX_FMT_GRAY14LE + } else { + AVPixelFormat::AV_PIX_FMT_GRAY14BE + } + } FrameFormat::Luma_16 | FrameFormat::Depth_16 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_GRAY16LE } else { - AVPixelFormat::AV_PIX_FMT_GRAY16BE } - }, + AVPixelFormat::AV_PIX_FMT_GRAY16LE + } else { + AVPixelFormat::AV_PIX_FMT_GRAY16BE + } + } FrameFormat::Rgb_3_3_2 => AVPixelFormat::AV_PIX_FMT_RGB8, FrameFormat::Rgb_5_6_5 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_RGB565LE } else { - AVPixelFormat::AV_PIX_FMT_RGB565BE } + AVPixelFormat::AV_PIX_FMT_RGB565LE + } else { + AVPixelFormat::AV_PIX_FMT_RGB565BE + } + } + FrameFormat::Rgb_5_5_5 => { + if is_little_endian() { + AVPixelFormat::AV_PIX_FMT_RGB555LE + } else { + AVPixelFormat::AV_PIX_FMT_RGB565BE + } } - FrameFormat::Rgb_5_5_5 => if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_RGB555LE } else { - AVPixelFormat::AV_PIX_FMT_RGB565BE } FrameFormat::Rgb_8_8_8 => AVPixelFormat::AV_PIX_FMT_RGB24, FrameFormat::Argb_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_ARGB, FrameFormat::Rgba_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_RGBA, FrameFormat::Bgr_3_3_2 => AVPixelFormat::AV_PIX_FMT_BGR8, FrameFormat::Bgr_5_6_5 => { if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_BGR565LE } else { - AVPixelFormat::AV_PIX_FMT_BGR565BE } + AVPixelFormat::AV_PIX_FMT_BGR565LE + } else { + AVPixelFormat::AV_PIX_FMT_BGR565BE + } + } + FrameFormat::Bgr_5_5_5 => { + if is_little_endian() { + AVPixelFormat::AV_PIX_FMT_BGR555LE + } else { + AVPixelFormat::AV_PIX_FMT_BGR555BE + } } - FrameFormat::Bgr_5_5_5 => if is_little_endian() { - AVPixelFormat::AV_PIX_FMT_BGR555LE } else { - AVPixelFormat::AV_PIX_FMT_BGR555BE } FrameFormat::Bgr_8_8_8 => AVPixelFormat::AV_PIX_FMT_BGR24, FrameFormat::Abgr_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_ABGR, FrameFormat::Bgra_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_BGRA, @@ -490,19 +639,10 @@ fn convert_frame_format_to_pixfmt(frame_format: &FrameFormat) -> AVPixelFormat { } } - #[derive(Clone, Debug)] pub struct FfmpegDecoderConfig { pub frame_format: FrameFormat, pub codec_tag: u32, - #[doc = " Extra binary data needed for initializing the decoder, codec-dependent.\n\n Must be allocated with av_malloc() and will be freed by\n avcodec_parameters_free(). The allocated size of extradata must be at\n least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding\n bytes zeroed."] - pub extra_data: Option<*mut u8>, - #[doc = " Size of the extradata content in bytes."] - pub extra_data_size: usize, - #[doc = " Additional data associated with the entire stream.\n\n Should be allocated with av_packet_side_data_new() or\n av_packet_side_data_add(), and will be freed by avcodec_parameters_free()."] - pub coded_side_data: Option<*mut AVPacketSideData>, - #[doc = " Amount of entries in @ref coded_side_data."] - pub coded_side_data_size: usize, #[doc = " - video: the pixel format, the value corresponds to enum AVPixelFormat.\n - audio: the sample format, the value corresponds to enum AVSampleFormat."] pub pix_fmt: Option, #[doc = " Codec-specific bitstream restrictions that the stream conforms to."] @@ -529,24 +669,24 @@ impl FfmpegDecoderConfig { pub fn with_camera_format(camera_format: &CameraFormat) -> Self { FfmpegDecoderConfig::from(*camera_format) } - + pub fn as_avcodec_params(&self) -> Result { let mut av_codec_params = unsafe { avcodec_parameters_alloc().read() }; av_codec_params.codec_type = AVMediaType::AVMEDIA_TYPE_VIDEO; - if let Some(extra_data) = self.extra_data { - if extra_data.is_null() { - return Err(NokhwaError::Decoder("extra data is nullptr!".to_string())) - } - av_codec_params.extradata = extra_data; - av_codec_params.extradata_size = self.extra_data_size as i32; - } - if let Some(side_data) = self.coded_side_data { - if side_data.is_null() { - return Err(NokhwaError::Decoder("side data is nullptr!".to_string())) - } - av_codec_params.coded_side_data = side_data; - av_codec_params.nb_coded_side_data = self.coded_side_data_size as i32; - } + // if let Some(extra_data) = self.extra_data { + // if extra_data.is_null() { + // return Err(NokhwaError::Decoder("extra data is nullptr!".to_string())) + // } + // av_codec_params.extradata = extra_data; + // av_codec_params.extradata_size = self.extra_data_size as i32; + // } + // if let Some(side_data) = self.coded_side_data { + // if side_data.is_null() { + // return Err(NokhwaError::Decoder("side data is nullptr!".to_string())) + // } + // av_codec_params.coded_side_data = side_data; + // av_codec_params.nb_coded_side_data = self.coded_side_data_size as i32; + // } if let Some(id) = convert_format_to_codec_id(&self.frame_format) { av_codec_params.codec_id = id.into(); @@ -558,7 +698,9 @@ impl FfmpegDecoderConfig { av_codec_params.format = pixel_format as i32; } } else { - return Err(NokhwaError::Decoder("Failed to convert frameformat to id".to_string())) + return Err(NokhwaError::DecoderInvalidConfiguration( + "Failed to convert frameformat to id".to_string(), + )); } av_codec_params.profile = self.profile; @@ -569,7 +711,10 @@ impl FfmpegDecoderConfig { av_codec_params.sample_aspect_ratio = self.sample_aspect_ratio; - av_codec_params.framerate = AVRational { num: self.frame_rate.numerator(), den: self.frame_rate.numerator() }; + av_codec_params.framerate = AVRational { + num: self.frame_rate.numerator(), + den: self.frame_rate.numerator(), + }; av_codec_params.field_order = self.field_order; @@ -584,10 +729,10 @@ impl FfmpegDecoderConfig { av_codec_params.chroma_location = self.chroma_location; av_codec_params.video_delay = self.video_delay; - + Ok(av_codec_params) } - + /// SAFETY: the user is responsible for freeing this return value pub fn as_ptr(&self) -> Result<*mut AVCodecParameters, NokhwaError> { Ok(&mut self.as_avcodec_params()?) @@ -597,18 +742,18 @@ impl FfmpegDecoderConfig { impl From for FfmpegDecoderConfig { fn from(value: CameraFormat) -> Self { Self { - frame_format: *value.format(), + frame_format: value.format(), codec_tag: 0, - extra_data: None, - extra_data_size: 0, - coded_side_data: None, - coded_side_data_size: 0, + // extra_data: None, + // extra_data_size: 0, + // coded_side_data: None, + // coded_side_data_size: 0, pix_fmt: None, profile: 0, level: 0, - resolution: *value.resolution(), + resolution: value.resolution(), sample_aspect_ratio: AVRational { num: 0, den: 1 }, - frame_rate: *value.frame_rate(), + frame_rate: value.frame_rate(), field_order: AVFieldOrder::AV_FIELD_UNKNOWN, color_range: AVColorRange::AVCOL_RANGE_UNSPECIFIED, color_primaries: AVColorPrimaries::AVCOL_PRI_RESERVED0, @@ -638,7 +783,7 @@ fn dealloc_av_params(avcodec_parameters: &mut Option<*mut AVCodecParameters>) { impl Drop for FfmpegCodec { fn drop(&mut self) { - let _ = self.deinitialize(); + let _ = self.deinitialize(); dealloc_av_params(&mut self.temp_cfg); unsafe { let mut ctx = self.decoder.as_mut_ptr(); diff --git a/nokhwa-decoders/src/general.rs b/nokhwa-decoders/src/general.rs new file mode 100644 index 0000000..e69de29 diff --git a/nokhwa-decoders/src/lib.rs b/nokhwa-decoders/src/lib.rs index 11b92af..3661011 100644 --- a/nokhwa-decoders/src/lib.rs +++ b/nokhwa-decoders/src/lib.rs @@ -1,2 +1,8 @@ #[cfg(feature = "ffmpeg")] -pub mod ffmpeg; \ No newline at end of file +pub mod ffmpeg; +#[cfg(feature = "general")] +pub mod general; +#[cfg(feature = "mjpeg")] +pub mod mjpeg; +#[cfg(feature = "yuyv")] +pub mod yuv; diff --git a/nokhwa-decoders/src/mjpeg.rs b/nokhwa-decoders/src/mjpeg.rs new file mode 100644 index 0000000..09aaad2 --- /dev/null +++ b/nokhwa-decoders/src/mjpeg.rs @@ -0,0 +1,199 @@ +use zune_core::bytestream::ZCursor; +use zune_core::colorspace::ColorSpace; +pub use zune_core::options::DecoderOptions; +use zune_jpeg::errors::DecodeErrors; +pub use zune_jpeg::ImageInfo; +use zune_jpeg::JpegDecoder; +use nokhwa_core::codec::Codec; +use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel}; +use nokhwa_core::error::NokhwaError; +use nokhwa_core::frame_buffer::FrameBuffer; +use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth}; +use nokhwa_core::types::Resolution; + +#[derive(Clone, Debug)] +pub struct MJpegDecoder { + config: MJpegOptions, +} + +impl Decoder for MJpegDecoder { + type Config = MJpegOptions; + type OutputMeta = ImageMeta; + type DestinationFormatHint = OutputColor; + + fn config(&self) -> &Self::Config { + &self.config + } + + fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> { + self.config = config; + Ok(()) + } + + fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option) -> Result { + let buffer = buffer.as_mut(); + let cursor = ZCursor::new(to_decode.as_ref()); + + let mut decoder = JpegDecoder::new(cursor); + + let mut config = self.config.decoder_options; + if let Some(dest_hint) = destination_format_hint { + config = self.config.decoder_options.jpeg_set_out_colorspace(dest_hint.into()); + } + + decoder.set_options(config); + decoder.decode_into(buffer).map_err(err_to_err)?; + + let info = match decoder.info() { + Some(i) => i, + + None => return Err(NokhwaError::Decoder("??????? how did we get here".to_string())) + }; + + Ok(info.into()) + } + + fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>) -> Result + where +

::Subpixel: NonFloatScalarWidth + { + let hint = match pixel_to_colorspace::

() { + Some(cs) => cs, + None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)) + }; + let meta = self.decode_to_buffer(to_decode, buffer, Some(hint))?; + Ok(meta) + } + + fn decode(&mut self, to_decode: FrameBuffer) -> Result, NokhwaError> + where +

::Subpixel: NonFloatScalarWidth + { + let min_size = self.output_decoder_min_size_pixel::

(self.config.resolution); + let mut buffer = vec![0_u8; min_size]; + let output_metadata = self.decode_to_pixel_buffer(to_decode, buffer.as_mut_slice())?; + let image_buffer = ImageBuffer::from_raw(output_metadata.resolution.width(), output_metadata.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to make imagebuffer".to_string()))?; + Ok(DecodedImage::new( + image_buffer, + output_metadata + )) + } + + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + let stride = match destination_format { + OutputColor::Rgb => 3, + OutputColor::RgbA => 4, + OutputColor::Bgr => 3, + OutputColor::BgrA => 4, + OutputColor::Luma => 1, + OutputColor::LumaA => 2, + }; + (resolution.width() * resolution.height() * stride) as usize + } + + fn buffer_takes_destination_hint(&self) -> bool { + true + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ImageMeta { + pub resolution: Resolution, + pub pixel_density: u8, + pub x_density: u16, + pub y_density: u16, + pub components: u8, + pub multi_picture_information: Option>, +} + +impl From for ImageMeta { + fn from(value: ImageInfo) -> Self { + Self { + resolution: Resolution::new(value.width as u32, value.width as u32), + pixel_density: value.pixel_density, + x_density: value.x_density, + y_density: value.y_density, + components: value.components, + multi_picture_information: value.multi_picture_information, + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct MJpegOptions { + pub resolution: Resolution, + pub decoder_options: DecoderOptions +} + +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +pub enum OutputColor { + Rgb, + RgbA, + Bgr, + BgrA, + Luma, + LumaA, +} + +impl Into for OutputColor { + fn into(self) -> ColorSpace { + match self { + OutputColor::Rgb => ColorSpace::RGB, + OutputColor::RgbA => ColorSpace::RGBA, + OutputColor::Bgr => ColorSpace::BGR, + OutputColor::BgrA => ColorSpace::BGRA, + OutputColor::Luma => ColorSpace::Luma, + OutputColor::LumaA => ColorSpace::LumaA, + } + } +} + +fn pixel_to_colorspace

() -> Option where P: Pixel,

::Subpixel: NonFloatScalarWidth { + match P::COLOR_MODEL { + "RGBA" => Some(OutputColor::RgbA), + "RGB" => Some(OutputColor::Rgb), + "BGR" => Some(OutputColor::Bgr), + "BGRA" => Some(OutputColor::BgrA), + "Y" => Some(OutputColor::Luma), + "YA" => Some(OutputColor::LumaA), + _ => None, + } +} + +fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { + match decode_errors { + DecodeErrors::Format(fmt) => { + NokhwaError::Decoder(fmt) + } + DecodeErrors::FormatStatic(fmt) => { + NokhwaError::Decoder(fmt.to_string()) + } + DecodeErrors::IllegalMagicBytes(b) => { + NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}")) + } + DecodeErrors::HuffmanDecode(huff) => { + NokhwaError::DecoderInvalidFrameData(format!("bad huffman tables: {huff}")) + } + DecodeErrors::ZeroError => { + NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string()) + } + DecodeErrors::DqtError(e) | DecodeErrors::MCUError(e) | DecodeErrors::SosError(e) | DecodeErrors::SofError(e)=> { + NokhwaError::Decoder(format!("error decoding: {e}")) + } + DecodeErrors::Unsupported(why) => { + NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}")) + } + DecodeErrors::ExhaustedData => { + NokhwaError::DecoderInvalidBuffer("image data exhausted".to_string()) + } + DecodeErrors::LargeDimensions(size) => { + NokhwaError::DecoderInvalidBuffer(format!("too large: {size}")) + } + DecodeErrors::TooSmallOutput(a, b) => { + NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}")) + } + DecodeErrors::IoErrors(io) => { + NokhwaError::Decoder(format!("io error: {io:?}")) + } + } +} diff --git a/nokhwa-decoders/src/yuv.rs b/nokhwa-decoders/src/yuv.rs new file mode 100644 index 0000000..2a3986d --- /dev/null +++ b/nokhwa-decoders/src/yuv.rs @@ -0,0 +1,471 @@ +use bytemuck::{cast_slice, cast_slice_mut, try_cast_slice_mut}; +use nokhwa_core::error::NokhwaError; +use nokhwa_core::frame_buffer::FrameBuffer; +use nokhwa_core::frame_format::FrameFormat; +use nokhwa_core::types::{CameraFormat, Resolution}; +use yuv::{ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb, p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr, uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16, vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba, vyuy422_to_rgba_p16, yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba, yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr, yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra, yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb, yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra, yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange, YuvStandardMatrix}; +use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive}; +use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth}; + +pub struct YUVDecoder { + config: YUVConfig, + stride_cache: Option, +} + +impl Decoder for YUVDecoder { + type Config = YUVConfig; + type OutputMeta = (); + type DestinationFormatHint = YUVDestination; + + fn config(&self) -> &Self::Config { + &self.config + } + + fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> { + if !FrameFormat::YCBCR.contains(&config.yuv_type) { + return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type)) + } + self.config = config; + self.stride_cache = None; + Ok(()) + } + + fn decode_to_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, destination_format: Option) -> Result { + let destination_format = match destination_format { + Some(df) => df, + None => return Err(NokhwaError::DecoderDestinationHintRequired) + }; + + let buffer = buffer.as_mut(); + if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format) { + return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string())) + } + + let stride = match self.stride_cache { + Some(c) => c, + None => { + self.stride_cache = figure_out_stride(self.config.yuv_type); + match self.stride_cache { + Some(s) => s, + None => return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)), + } + } + }; + + // todo: clean up ts into a macro { + let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride); + match self.config.yuv_type { + FrameFormat::Ayuv_32 => { + match destination_format { + YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + _ => None, + } + } + FrameFormat::Yuyv_4_2_2 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, 8, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + } + } + FrameFormat::Uyvy_4_2_2 => { + match destination_format { + YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + } + } + FrameFormat::Vyuy_4_2_2 => { + match destination_format { + YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + } + } + FrameFormat::Yvyu_4_2_2 => { + match destination_format { + YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + } + } + _ => { + if FrameFormat::YCBCR_PACKED.contains(&self.config.yuv_type) { + return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string())) + } + // shouldnt happen + return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)) + } + } + } + CachedStride::SemiPlanar(y_stride, uv_stride) => { + let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, y_stride, uv_stride); + match self.config.yuv_type { + FrameFormat::NV24 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::NV42 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::NV16 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::NV61 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::NV12 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::NV21 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + _ => None, + } + } + FrameFormat::P010 => { + match destination_format { + YUVDestination::Rgb8 => Some(p010_to_rgb(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgba8 => Some(p010_to_rgba(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgr8 => Some(p010_to_bgr(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Bgra8 => Some(p010_to_bgra(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)), + YUVDestination::Rgb16 => Some(p010_to_rgb10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(p010_to_rgba10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)), + // _ => None, + } + } + FrameFormat::P012 => { + match destination_format { + YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)), + YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)), + _ => None, + } + } + _ => { + if FrameFormat::YCBCR_SEMI.contains(&self.config.yuv_type) { + return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string())) + } + // shouldnt happen + return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)) + } + } + } + CachedStride::Planar(y_stride, u_stride, v_stride) => { + let image = prepare_to_planar_image(&to_decode, self.config.resolution, y_stride, u_stride, v_stride); + match self.config.yuv_type { + FrameFormat::Yuv_4_2_0 => { + match destination_format { + YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)), + YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)), + YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)), + _ => None, + } + } + _ => { + if FrameFormat::YCBCR_PLANAR.contains(&self.config.yuv_type) { + return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string())) + } + // shouldnt happen + return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)) + } + } + } + }; + match decode_status { + Some(Ok(_)) => Ok(()), + Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())), + None => Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)), + } + } + + fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[P::Subpixel]>) -> Result + where +

::Subpixel: NonFloatScalarWidth + { + let destination = match YUVDestination::get_by_pixel::

() { + None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)), + Some(d) => d, + }; + let buffer = buffer.as_mut(); + + let cast_slice = try_cast_slice_mut::(buffer) + .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?; + + self.decode_to_buffer(to_decode, cast_slice, Some(destination)) + } + + fn decode(&mut self, to_decode: FrameBuffer<'_>) -> Result, NokhwaError> + where +

::Subpixel: NonFloatScalarWidth + { + let min_size_alloc = self.output_decoder_min_size_pixel::

(self.config.resolution); + let mut out_buffer: Vec = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc]; + self.decode_to_pixel_buffer::

(to_decode, &mut out_buffer)?; + Ok( + DecodedImage::new( + ImageBuffer::from_vec(self.config.resolution.width(), self.config.resolution.height(), out_buffer) + .ok_or(NokhwaError::Decoder("failed to convert into an image buffer".to_string()))?, + () + ) + ) + } + + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + let px_size = match destination_format { + YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3, + YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4, + YUVDestination::Rgb16 => 3 * 2, + YUVDestination::Rgba16 => 4 * 2, + }; + let reso = resolution.width() * resolution.height(); + (reso as usize) * (px_size as usize) + } + + fn buffer_takes_destination_hint(&self) -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +pub enum YUVDestination { + Rgb8, + Rgba8, + Rgb16, + Rgba16, + Bgr8, + Bgra8, +} + +impl YUVDestination { + pub fn get_by_pixel

() -> Option where P: Pixel,

::Subpixel: NonFloatScalarWidth { + match P::COLOR_MODEL { + "RGB" => { + match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + 1 => Some(YUVDestination::Rgb8), + 2 => Some(YUVDestination::Rgb16), + _ => None, + } + } + "RGBA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + 1 => Some(YUVDestination::Rgba8), + 2 => Some(YUVDestination::Rgba16), + _ => None, + } + "BGR" =>match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + 1 => Some(YUVDestination::Bgr8), + // 2 => Some(YUVDestination::Bgr16), + _ => None, + } + "BGRA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { + 1 => Some(YUVDestination::Bgra8), + // 2 => Some(YUVDestination::Bgra16), + _ => None, + } + _ => None, + } + } +} + +#[derive(Clone, Debug, PartialOrd, PartialEq)] +pub struct YUVConfig { + pub resolution: Resolution, + pub yuv_type: FrameFormat, + pub range: YuvRange, + pub matrix: YuvStandardMatrix, + pub mode: YuvConversionMode, + pub premultiply_alpha: bool, +} + +impl TryFrom for YUVConfig { + type Error = NokhwaError; + + fn try_from(value: CameraFormat) -> Result { + if !FrameFormat::YCBCR.contains(&value.format()) { + return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format())) + } + Ok(YUVConfig { + resolution: value.resolution(), + yuv_type: value.format(), + range: YuvRange::Full, + matrix: YuvStandardMatrix::Bt601, + mode: YuvConversionMode::Balanced, + premultiply_alpha: false, + }) + } +} + +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +enum CachedStride { + Packed(u32), + SemiPlanar(u32, u32), + Planar(u32, u32, u32), +} + +fn figure_out_stride(frame_format: FrameFormat) -> Option { + if let Some(s) = packed_stride_component_per_4px(frame_format) { + return Some(CachedStride::Packed(s)); + } + if let Some((s1, s2)) = semiplanar_stride_per_4px(frame_format) { + return Some(CachedStride::SemiPlanar(s1, s2)); + } + if let Some((s1, s2, s3)) = planar_stride_per_4px(frame_format) { + return Some(CachedStride::Planar(s1, s2, s3)); + } + None +} + +fn packed_stride_component_per_4px(format: FrameFormat) -> Option { + match format { + FrameFormat::Ayuv_32 => Some(64), + FrameFormat::Yuyv_4_2_2 + | FrameFormat::Uyvy_4_2_2 + | FrameFormat::Vyuy_4_2_2 + | FrameFormat::Yvyu_4_2_2 => Some(16), + _ => None, + } +} + +fn prepare_to_packed_image<'a>( + frame_buffer: &'a FrameBuffer<'a>, + resolution: Resolution, + yuy_stride: u32, +) -> YuvPackedImage<'a, u8> { + YuvPackedImage { + yuy: frame_buffer.buffer(), + yuy_stride, + width: resolution.width(), + height: resolution.height(), + } +} + +fn semiplanar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32)> { + match format { + FrameFormat::NV24 | FrameFormat::NV42 => Some((4, 8)), + FrameFormat::NV16 | FrameFormat::NV61 => Some((4, 4)), + FrameFormat::NV12 | FrameFormat::NV21 | FrameFormat::P010 | FrameFormat::P012 => { + Some((4, 4)) + } + _ => None, + } +} + +fn prepare_to_semi_planar_image<'a>( + frame_buffer: &'a FrameBuffer<'a>, + resolution: Resolution, + y_stride: u32, + uv_stride: u32, +) -> YuvBiPlanarImage<'a, u8> { + let uv_start = (resolution.width() * resolution.height()) as usize; + YuvBiPlanarImage { + y_plane: &frame_buffer.buffer()[0..uv_start], + y_stride, + uv_plane: &frame_buffer.buffer()[uv_start..frame_buffer.len()], + uv_stride, + width: resolution.width(), + height: resolution.height(), + } +} + +fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> { + match format { + FrameFormat::Yuv_4_2_0 => Some((4, 1, 1)), + _ => None, + } +} + +fn prepare_to_planar_image<'a>( + frame_buffer: &'a FrameBuffer<'a>, + resolution: Resolution, + y_stride: u32, + u_stride: u32, + v_stride: u32, +) -> YuvPlanarImage<'a, u8> { + let u_start = (resolution.width() * resolution.height()) as usize; + let v_start = + u_start + ((resolution.width() / 4) * y_stride * (resolution.height() * u_stride)) as usize; + YuvPlanarImage { + y_plane: &frame_buffer.buffer()[0..u_start], + y_stride, + u_plane: &frame_buffer.buffer()[u_start..v_start], + u_stride, + v_plane: &frame_buffer.buffer()[v_start..frame_buffer.len()], + v_stride, + width: resolution.width(), + height: resolution.height(), + } +} + +fn convert_packed_image_to_u16( + yuv_packed_image: YuvPackedImage, +) -> YuvPackedImage { + let buf= cast_slice(yuv_packed_image.yuy); + YuvPackedImage { + yuy: buf, + yuy_stride: yuv_packed_image.yuy_stride, + width: yuv_packed_image.width, + height: yuv_packed_image.height, + } +} + +fn convert_bi_planar_image_to_u16( + yuv_bi_planar_image: YuvBiPlanarImage, +) -> YuvBiPlanarImage { + let buf_y= cast_slice(yuv_bi_planar_image.y_plane); + let buf_uv= cast_slice(yuv_bi_planar_image.uv_plane); + YuvBiPlanarImage { + y_plane: buf_y, + y_stride: yuv_bi_planar_image.y_stride, + uv_plane: buf_uv, + uv_stride: yuv_bi_planar_image.uv_stride, + width: yuv_bi_planar_image.width, + height: yuv_bi_planar_image.height, + } +}