From 271d64a4a866c42aa4608a9b60aba0116ea3a92d Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Mon, 13 Feb 2023 21:52:05 +0900 Subject: [PATCH] new frameformat, new filter, new request --- nokhwa-core/Cargo.toml | 6 +- nokhwa-core/src/buffer.rs | 11 +- nokhwa-core/src/format_filter.rs | 105 +++------- nokhwa-core/src/frame_format.rs | 87 ++++----- nokhwa-core/src/traits.rs | 23 +-- nokhwa-core/src/types.rs | 326 +++++++------------------------ 6 files changed, 157 insertions(+), 401 deletions(-) diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index e6a1bcd..9cec9f9 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -16,7 +16,7 @@ serialize = ["serde"] wgpu-types = ["wgpu"] mjpeg = ["mozjpeg"] opencv-mat = ["opencv"] -docs-features = ["serialize", "wgpu-types", "opencv/docs-only", "mjpeg"] +docs-features = ["serialize", "wgpu-types", "mjpeg"] test-fail-warnings = [] @@ -34,11 +34,11 @@ features = ["derive"] optional = true [dependencies.wgpu] -version = "0.14" +version = "0.15" optional = true [dependencies.opencv] -version = "0.74" +version = "0.76" default-features = false optional = true diff --git a/nokhwa-core/src/buffer.rs b/nokhwa-core/src/buffer.rs index 9632fce..b75d475 100644 --- a/nokhwa-core/src/buffer.rs +++ b/nokhwa-core/src/buffer.rs @@ -14,9 +14,8 @@ * limitations under the License. */ -use crate::{error::NokhwaError, frame_format::FrameFormat, types::Resolution}; +use crate::{frame_format::SourceFrameFormat, types::Resolution}; use bytes::Bytes; -use image::ImageBuffer; /// A buffer returned by a camera to accommodate custom decoding. /// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer. @@ -26,14 +25,14 @@ use image::ImageBuffer; pub struct Buffer { resolution: Resolution, buffer: Bytes, - source_frame_format: FrameFormat, + source_frame_format: SourceFrameFormat, } impl Buffer { /// Creates a new buffer with a [`&[u8]`]. #[must_use] #[inline] - pub fn new(res: Resolution, buf: &[u8], source_frame_format: FrameFormat) -> Self { + pub fn new(res: Resolution, buf: &[u8], source_frame_format: SourceFrameFormat) -> Self { Self { resolution: res, buffer: Bytes::copy_from_slice(buf), @@ -59,9 +58,9 @@ impl Buffer { self.buffer.clone() } - /// Get the [`FrameFormat`] of this buffer. + /// Get the [`SourceFrameFormat`] of this buffer. #[must_use] - pub fn source_frame_format(&self) -> FrameFormat { + pub fn source_frame_format(&self) -> SourceFrameFormat { self.source_frame_format } diff --git a/nokhwa-core/src/format_filter.rs b/nokhwa-core/src/format_filter.rs index e5b0231..722b223 100644 --- a/nokhwa-core/src/format_filter.rs +++ b/nokhwa-core/src/format_filter.rs @@ -1,6 +1,8 @@ -use crate::frame_format::FrameFormat; -use crate::types::{ApiBackend, CameraFormat, Resolution}; -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use crate::{ + frame_format::FrameFormat, + types::{ApiBackend, CameraFormat, Resolution}, +}; +use std::collections::{BTreeMap, BTreeSet}; /// Tells the init function what camera format to pick. /// - `AbsoluteHighestResolution`: Pick the highest [`Resolution`], then pick the highest frame rate of those provided. @@ -22,6 +24,7 @@ pub enum RequestedFormatType { None, } +/// How you get your [`FrameFormat`] from the #[derive(Clone, Debug)] pub struct FormatFilter { filter_pref: RequestedFormatType, @@ -48,23 +51,29 @@ impl FormatFilter { pub fn add_allowed_platform_specific(&mut self, platform: ApiBackend, frame_format: u128) { match self.fcc_platform.get_mut(&platform) { - Some(fccs) => fccs.push(frame_format), - None => self - .fcc_platform - .insert(platform, BTreeSet::from([frame_format])), + Some(fccs) => { + fccs.insert(frame_format); + } + None => { + self.fcc_platform + .insert(platform, BTreeSet::from([frame_format])); + } }; } pub fn add_allowed_platform_specific_many( &mut self, - platform_specifics: impl IntoPlatformSpecificMany, + platform_specifics: impl AsRef<[(ApiBackend, u128)]>, ) { - for (platform, frame_format) in platform_specifics.into_psm().into_iter() { + for (platform, frame_format) in platform_specifics.as_ref().into_iter() { match self.fcc_platform.get_mut(&platform) { - Some(fccs) => fccs.push(frame_format), - None => self - .fcc_platform - .insert(platform, BTreeSet::from([frame_format])), + Some(fccs) => { + fccs.insert(*frame_format); + } + None => { + self.fcc_platform + .insert(*platform, BTreeSet::from([*frame_format])); + } }; } } @@ -87,18 +96,13 @@ impl FormatFilter { platform: ApiBackend, frame_format: u128, ) -> Self { - match self.fcc_platform.get_mut(&platform) { - Some(fccs) => fccs.push(frame_format), - None => self - .fcc_platform - .insert(platform, BTreeSet::from([frame_format])), - }; + self.add_allowed_platform_specific(platform, frame_format); self } pub fn with_allowed_platform_specific_many( mut self, - platform_specifics: impl IntoPlatformSpecificMany, + platform_specifics: impl AsRef<[(ApiBackend, u128)]>, ) -> Self { self.add_allowed_platform_specific_many(platform_specifics); self @@ -119,63 +123,4 @@ impl Default for FormatFilter { } } -pub trait IntoPlatformSpecificMany { - fn into_psm(self) -> Vec<(ApiBackend, u128)>; -} - -impl IntoPlatformSpecificMany for T -where - T: AsRef<(ApiBackend, u128)>, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - vec![*self.as_ref()] - } -} -impl IntoPlatformSpecificMany for T -where - T: AsRef<[(ApiBackend, u128)]>, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - self.as_ref().iter().collect() - } -} - -impl IntoPlatformSpecificMany for BTreeMap -where - T: AsRef<[u128]>, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - self.into_iter() - .flat_map(|(backend, fourcc_lst)| fourcc_lst.as_ref().iter().map(|fcc| (backend, *fcc))) - .collect() - } -} - -impl IntoPlatformSpecificMany for HashMap -where - T: AsRef<[u128]>, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - self.into_iter() - .flat_map(|(backend, fourcc_lst)| fourcc_lst.as_ref().iter().map(|fcc| (backend, *fcc))) - .collect() - } -} - -impl IntoPlatformSpecificMany for &T -where - T: IntoPlatformSpecificMany + Clone, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - self.clone().into_psm() - } -} - -impl IntoPlatformSpecificMany for &mut T -where - T: IntoPlatformSpecificMany + Clone, -{ - fn into_psm(self) -> Vec<(ApiBackend, u128)> { - self.clone().into_psm() - } -} +pub struct FormatFulfill {} diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 955e54f..c848852 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -1,6 +1,26 @@ -use crate::error::NokhwaError; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; +/* + * Copyright 2022 l1npengtul / The Nokhwa Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::{buffer::Buffer, types::ApiBackend}; +use image::{ImageBuffer, Pixel}; +use std::{ + error::Error, + fmt::{Display, Formatter}, + ops::Deref, +}; /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`. #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] @@ -103,55 +123,34 @@ impl Display for FrameFormat { } } -/* - * Copyright 2022 l1npengtul / The Nokhwa Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -use crate::buffer::Buffer; -use crate::types::ApiBackend; -use image::{ImageBuffer, Luma, LumaA, Primitive, Rgb, Rgba}; -use std::ops::Deref; +/// The Source Format of a [`Buffer`]. +/// +/// May either be a platform specific FourCC, or a FrameFormat +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum SourceFrameFormat { + FrameFormat(FrameFormat), + PlatformSpecific(ApiBackend, u128), +} -pub trait FormatDecoders: Send + Sync { +impl Display for SourceFrameFormat { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +pub trait FormatDecoders: Send + Sync { const NAME: &'static str; const PRIMARY: &'static [FrameFormat]; const PLATFORM_ACCEPTABLE: &'static [(ApiBackend, &'static [u128])]; - type Primitive: Primitive; + type Container: Deref; - type Container: Deref; -} - -pub trait RgbDecoder: FormatDecoders { - fn decode_rgb(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; -} - -pub trait RgbADecoder: FormatDecoders { - fn decode_rgba(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; -} - -pub trait LumaDecoder: FormatDecoders { - fn decode_luma(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; -} - -pub trait LumaADecoder: FormatDecoders { - fn decode_luma_a( - &self, - buffer: &Buffer, - ) -> ImageBuffer, Self::Container>; + fn decode(&self, buffer: &Buffer) -> Result, E>; } // TODO: Wgpu Decoder + +// TODO: OpenCV Mat Decoder diff --git a/nokhwa-core/src/traits.rs b/nokhwa-core/src/traits.rs index 90bee82..595ee5a 100644 --- a/nokhwa-core/src/traits.rs +++ b/nokhwa-core/src/traits.rs @@ -14,23 +14,18 @@ * limitations under the License. */ -use crate::frame_format::FrameFormat; -use crate::types::RequestedFormat; +use crate::frame_format::SourceFrameFormat; use crate::{ buffer::Buffer, error::NokhwaError, + format_filter::FormatFilter, + frame_format::FrameFormat, types::{ ApiBackend, CameraControl, CameraFormat, CameraInfo, ControlValueSetter, KnownCameraControl, Resolution, }, }; use std::{borrow::Cow, collections::HashMap}; -#[cfg(feature = "wgpu-types")] -use wgpu::{ - Device as WgpuDevice, Extent3d, ImageCopyTexture, ImageDataLayout, Queue as WgpuQueue, - Texture as WgpuTexture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, - TextureUsages, -}; /// This trait is for any backend that allows you to grab and take frames from a camera. /// Many of the backends are **blocking**, if the camera is occupied the library will block while it waits for it to become available. @@ -43,8 +38,8 @@ pub trait CaptureBackendTrait { /// Initialize the camera, preparing it for use, with a random format (usually the first one). fn init(&mut self) -> Result<(), NokhwaError>; - /// Initialize the camera, preparing it for use, with a format that fits the supplied [`RequestedFormat`]. - fn init_with_format(&mut self, format: RequestedFormat) -> Result; + /// Initialize the camera, preparing it for use, with a format that fits the supplied [`FormatFilter`]. + fn init_with_format(&mut self, format: FormatFilter) -> Result; /// Returns the current backend used. fn backend(&self) -> ApiBackend; @@ -70,10 +65,10 @@ pub trait CaptureBackendTrait { /// A hashmap of [`Resolution`]s mapped to framerates. Not sorted! /// # Errors - /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](crate::error::NokhwaError::UnsupportedOperationError)). + /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](NokhwaError::UnsupportedOperationError)). fn compatible_list_by_resolution( &mut self, - fourcc: FrameFormat, + fourcc: SourceFrameFormat, ) -> Result>, NokhwaError>; /// Gets the compatible [`CameraFormat`] of the camera @@ -94,8 +89,8 @@ pub trait CaptureBackendTrait { /// A Vector of compatible [`FrameFormat`]s. Will only return 2 elements at most. /// # Errors - /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](crate::error::NokhwaError::UnsupportedOperationError)). - fn compatible_fourcc(&mut self) -> Result, NokhwaError>; + /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`UnsupportedOperationError`](NokhwaError::UnsupportedOperationError)). + fn compatible_fourcc(&mut self) -> Result, NokhwaError>; /// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]). This will force refresh to the current latest if it has changed. fn resolution(&self) -> Resolution; diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 5037b38..cda1526 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -1,12 +1,14 @@ -use crate::frame_format::FrameFormat; -use crate::{error::NokhwaError, pixel_format::FormatDecoders}; +use crate::{ + error::NokhwaError, + format_filter::RequestedFormatType, + frame_format::{FrameFormat, SourceFrameFormat}, +}; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use std::{ borrow::Borrow, cmp::Ordering, fmt::{Display, Formatter}, - str::FromStr, }; impl Default for RequestedFormatType { @@ -21,177 +23,6 @@ impl Display for RequestedFormatType { } } -/// A request to the camera for a valid [`CameraFormat`] -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct RequestedFormat<'a> { - requested_format: RequestedFormatType, - wanted_decoder: &'a [FrameFormat], -} - -impl RequestedFormat<'_> { - /// Creates a new [`RequestedFormat`] by using the [`RequestedFormatType`] and getting the [`FrameFormat`] - /// constraints from a generic type. - #[must_use] - pub fn new( - requested: RequestedFormatType, - ) -> RequestedFormat<'static> { - RequestedFormat { - requested_format: requested, - wanted_decoder: Decoder::FORMATS, - } - } - - /// Creates a new [`RequestedFormat`] by using the [`RequestedFormatType`] and getting the [`FrameFormat`] - /// constraints from a statically allocated slice. - #[must_use] - pub fn with_formats( - requested: RequestedFormatType, - decoder: &[FrameFormat], - ) -> RequestedFormat { - RequestedFormat { - requested_format: requested, - wanted_decoder: decoder, - } - } - - /// Gets the [`RequestedFormatType`] - #[must_use] - pub fn requested_format_type(&self) -> RequestedFormatType { - self.requested_format - } - - /// Fulfill the requested using a list of all available formats. - /// - /// See [`RequestedFormatType`] for more details. - #[must_use] - #[allow(clippy::too_many_lines)] - pub fn fulfill(&self, all_formats: &[CameraFormat]) -> Option { - match self.requested_format { - RequestedFormatType::AbsoluteHighestResolution => { - let mut formats = all_formats.to_vec(); - formats.sort_by_key(CameraFormat::resolution); - let resolution = *formats.iter().last()?; - let mut format_resolutions = formats - .into_iter() - .filter(|fmt| { - fmt.resolution() == resolution.resolution() - && self.wanted_decoder.contains(&fmt.format()) - }) - .collect::>(); - format_resolutions.sort_by_key(CameraFormat::frame_rate); - format_resolutions.last().copied() - } - RequestedFormatType::AbsoluteHighestFrameRate => { - let mut formats = all_formats.to_vec(); - formats.sort_by_key(CameraFormat::frame_rate); - let frame_rate = *formats.iter().last()?; - let mut format_framerates = formats - .into_iter() - .filter(|fmt| { - fmt.frame_rate() == frame_rate.frame_rate() - && self.wanted_decoder.contains(&fmt.format()) - }) - .collect::>(); - format_framerates.sort_by_key(CameraFormat::resolution); - format_framerates.last().copied() - } - RequestedFormatType::HighestResolution(res) => { - let mut formats = all_formats - .iter() - .filter(|x| x.resolution == res) - .copied() - .collect::>(); - formats.sort_by(|a, b| a.frame_rate.cmp(&b.frame_rate)); - let highest_fps = match formats.last() { - Some(cf) => cf.frame_rate, - None => return None, - }; - formats - .into_iter() - .filter(|x| x.frame_rate == highest_fps) - .last() - } - RequestedFormatType::HighestFrameRate(fps) => { - let mut formats = all_formats - .iter() - .filter(|x| x.frame_rate == fps) - .copied() - .collect::>(); - formats.sort_by(|a, b| a.resolution.cmp(&b.resolution)); - let highest_res = match formats.last() { - Some(cf) => cf.resolution, - None => return None, - }; - formats - .into_iter() - .filter(|x| x.resolution() == highest_res) - .last() - } - RequestedFormatType::Exact(fmt) => { - if self.wanted_decoder.contains(&fmt.format()) { - Some(fmt) - } else { - None - } - } - #[allow(clippy::cast_possible_wrap)] - RequestedFormatType::Closest(c) => { - let same_fmt_formats = all_formats - .iter() - .filter(|x| { - x.format() == c.format() && self.wanted_decoder.contains(&x.format()) - }) - .copied() - .collect::>(); - let mut resolution_map = same_fmt_formats - .iter() - .map(|x| { - let res = x.resolution(); - let x_diff = res.x() as i32 - c.resolution().x() as i32; - let y_diff = res.y() as i32 - c.resolution().y() as i32; - let dist_no_sqrt = (x_diff.abs()).pow(2) + (y_diff.abs()).pow(2); - (dist_no_sqrt, res) - }) - .collect::>(); - resolution_map.sort_by(|a, b| a.0.cmp(&b.0)); - resolution_map.dedup_by(|a, b| a.0.eq(&b.0)); - let resolution = resolution_map.first()?.1; - - let frame_rates = all_formats - .iter() - .filter_map(|cfmt| { - if cfmt.format() == c.format() && cfmt.resolution() == c.resolution() { - return Some(cfmt.frame_rate()); - } - None - }) - .collect::>(); - // sort FPSes - let mut framerate_map = frame_rates - .iter() - .map(|x| { - let abs = *x as i32 - c.frame_rate() as i32; - (abs.unsigned_abs(), *x) - }) - .collect::>(); - framerate_map.sort_by(|a, b| a.0.cmp(&b.0)); - let frame_rate = framerate_map.first()?.1; - Some(CameraFormat::new(resolution, c.format(), frame_rate)) - } - RequestedFormatType::None => all_formats - .iter() - .find(|fmt| self.wanted_decoder.contains(&fmt.format())) - .copied(), - } - } -} - -impl Display for RequestedFormat<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - /// Describes the index of the camera. /// - Index: A numbered index /// - String: A string, used for `IPCameras`. @@ -360,14 +191,14 @@ impl Ord for Resolution { #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct CameraFormat { resolution: Resolution, - format: FrameFormat, + format: SourceFrameFormat, frame_rate: u32, } impl CameraFormat { /// Construct a new [`CameraFormat`] #[must_use] - pub fn new(resolution: Resolution, format: FrameFormat, frame_rate: u32) -> Self { + pub fn new(resolution: Resolution, format: SourceFrameFormat, frame_rate: u32) -> Self { CameraFormat { resolution, format, @@ -377,7 +208,7 @@ impl CameraFormat { /// [`CameraFormat::new()`], but raw. #[must_use] - pub fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: u32) -> Self { + pub fn new_from(res_x: u32, res_y: u32, format: SourceFrameFormat, fps: u32) -> Self { CameraFormat { resolution: Resolution { width_x: res_x, @@ -424,12 +255,12 @@ impl CameraFormat { /// Get the [`CameraFormat`]'s format. #[must_use] - pub fn format(&self) -> FrameFormat { + pub fn format(&self) -> SourceFrameFormat { self.format } /// Set the [`CameraFormat`]'s format. - pub fn set_format(&mut self, format: FrameFormat) { + pub fn set_format(&mut self, format: SourceFrameFormat) { self.format = format; } } @@ -438,7 +269,7 @@ impl Default for CameraFormat { fn default() -> Self { CameraFormat { resolution: Resolution::new(640, 480), - format: FrameFormat::MJPEG, + format: SourceFrameFormat::FrameFormat(FrameFormat::MJpeg), frame_rate: 30, } } @@ -766,7 +597,7 @@ impl ControlValueDescription { } } - /// Verifies if the [setter](crate::types::ControlValueSetter) is valid for the provided [`ControlValueDescription`]. + /// Verifies if the [setter](ControlValueSetter) is valid for the provided [`ControlValueDescription`]. /// - `true` => Is valid. /// - `false` => Is not valid. /// @@ -1367,7 +1198,44 @@ impl Display for ApiBackend { // } // } -/// Converts a MJPEG stream of `&[u8]` into a `Vec` of RGB888. (R,G,B,R,G,B,...) +#[cfg(all(feature = "mjpeg", not(target_arch = "wasm")))] +#[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))] +#[inline] +fn decompress( + data: impl AsRef<[u8]>, + rgba: bool, +) -> Result { + use mozjpeg::Decompress; + + match Decompress::new_mem(data) { + Ok(decompress) => { + let decompressor_res = if rgba { + decompress.rgba() + } else { + decompress.rgb() + }; + match decompressor_res { + Ok(decompressor) => Ok(decompressor), + Err(why) => { + return Err(NokhwaError::ProcessFrameError { + src: FrameFormat::MJpeg, + destination: "RGB888".to_string(), + error: why.to_string(), + }) + } + } + } + Err(why) => { + return Err(NokhwaError::ProcessFrameError { + src: FrameFormat::MJpeg, + destination: "RGB888".to_string(), + error: why.to_string(), + }) + } + } +} + +/// Converts a MJpeg stream of `&[u8]` into a `Vec` of RGB888. (R,G,B,R,G,B,...) /// # Errors /// If `mozjpeg` fails to read scanlines or setup the decompressor, this will error. /// # Safety @@ -1379,38 +1247,13 @@ impl Display for ApiBackend { pub fn mjpeg_to_rgb(data: &[u8], rgba: bool) -> Result, NokhwaError> { use mozjpeg::Decompress; - let mut jpeg_decompress = match Decompress::new_mem(data) { - Ok(decompress) => { - let decompressor_res = if rgba { - decompress.rgba() - } else { - decompress.rgb() - }; - match decompressor_res { - Ok(decompressor) => decompressor, - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - } - } - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - }; + let mut jpeg_decompress = decompress(data, rgba)?; let scanlines_res: Option> = jpeg_decompress.read_scanlines_flat(); // assert!(jpeg_decompress.finish_decompress()); if !jpeg_decompress.finish_decompress() { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, + src: FrameFormat::MJpeg, destination: "RGB888".to_string(), error: "JPEG Decompressor did not finish.".to_string(), }); @@ -1419,7 +1262,7 @@ pub fn mjpeg_to_rgb(data: &[u8], rgba: bool) -> Result, NokhwaError> { match scanlines_res { Some(pixels) => Ok(pixels), None => Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, + src: FrameFormat::MJpeg, destination: "RGB888".to_string(), error: "Failed to get read readlines into RGB888 pixels!".to_string(), }), @@ -1435,44 +1278,19 @@ pub fn mjpeg_to_rgb(_data: &[u8], _rgba: bool) -> Result, NokhwaError> { /// Equivalent to [`mjpeg_to_rgb`] except with a destination buffer. /// # Errors -/// If the decoding fails (e.g. invalid MJPEG stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error. +/// If the decoding fails (e.g. invalid MJpeg stream), the buffer is not large enough, or you are doing this on `WebAssembly`, this will error. #[cfg(all(feature = "mjpeg", not(target_arch = "wasm")))] #[cfg_attr(feature = "docs-features", doc(cfg(feature = "mjpeg")))] #[inline] pub fn buf_mjpeg_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> { use mozjpeg::Decompress; - let mut jpeg_decompress = match Decompress::new_mem(data) { - Ok(decompress) => { - let decompressor_res = if rgba { - decompress.rgba() - } else { - decompress.rgb() - }; - match decompressor_res { - Ok(decompressor) => decompressor, - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - } - } - Err(why) => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, - destination: "RGB888".to_string(), - error: why.to_string(), - }) - } - }; + let mut jpeg_decompress = decompress(data, rgba)?; // assert_eq!(dest.len(), jpeg_decompress.min_flat_buffer_size()); if dest.len() != jpeg_decompress.min_flat_buffer_size() { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, + src: FrameFormat::MJpeg, destination: "RGB888".to_string(), error: "Bad decoded buffer size".to_string(), }); @@ -1482,7 +1300,7 @@ pub fn buf_mjpeg_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), // assert!(jpeg_decompress.finish_decompress()); if !jpeg_decompress.finish_decompress() { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::MJPEG, + src: FrameFormat::MJpeg, destination: "RGB888".to_string(), error: "JPEG Decompressor did not finish.".to_string(), }); @@ -1497,7 +1315,7 @@ pub fn buf_mjpeg_to_rgb(_data: &[u8], _dest: &mut [u8], _rgba: bool) -> Result<( )) } -/// Returns the predicted size of the destination YUYV422 buffer. +/// Returns the predicted size of the destination Yuv422422 buffer. #[inline] pub fn yuyv422_predicted_size(size: usize, rgba: bool) -> usize { let pixel_size = if rgba { 4 } else { 3 }; @@ -1508,10 +1326,10 @@ pub fn yuyv422_predicted_size(size: usize, rgba: bool) -> usize { // For those maintaining this, I recommend you read: https://docs.microsoft.com/en-us/windows/win32/medfound/recommended-8-bit-yuv-formats-for-video-rendering#yuy2 // https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB // and this too: https://stackoverflow.com/questions/16107165/convert-from-yuv-420-to-imagebgr-byte -// The YUY2(YUYV) format is a 16 bit format. We read 4 bytes at a time to get 6 bytes of RGB888. +// The YUY2(Yuv422) format is a 16 bit format. We read 4 bytes at a time to get 6 bytes of RGB888. // First, the YUY2 is converted to YCbCr 4:4:4 (4:2:2 -> 4:4:4) // then it is converted to 6 bytes (2 pixels) of RGB888 -/// Converts a YUYV 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) +/// Converts a Yuv422 4:2:2 datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) /// # Errors /// This may error when the data stream size is not divisible by 4, a i32 -> u8 conversion fails, or it fails to read from a certain index. #[inline] @@ -1528,12 +1346,12 @@ pub fn yuyv422_to_rgb(data: &[u8], rgba: bool) -> Result, NokhwaError> { /// Same as [`yuyv422_to_rgb`] but with a destination buffer instead of a return `Vec` /// # Errors -/// If the stream is invalid YUYV, or the destination buffer is not large enough, this will error. +/// If the stream is invalid Yuv422, or the destination buffer is not large enough, this will error. #[inline] pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<(), NokhwaError> { if data.len() % 4 != 0 { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::YUYV, + src: FrameFormat::Yuv422, destination: "RGB888".to_string(), error: "Assertion failure, the YUV stream isn't 4:2:2! (wrong number of bytes)" .to_string(), @@ -1546,7 +1364,7 @@ pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<() if dest.len() != rgb_buf_size { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::YUYV, + src: FrameFormat::Yuv422, destination: "RGB888".to_string(), error: format!("Assertion failure, the destination RGB buffer is of the wrong size! [expected: {rgb_buf_size}, actual: {}]", dest.len()), }); @@ -1571,9 +1389,9 @@ pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<() Some(v) => v, None => { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::YUYV, + src: FrameFormat::Yuv422, destination: "RGBA8888".to_string(), - error: "Ran out of RGBA YUYV values! (this should not happen, please file an issue: l1npengtul/nokhwa)".to_string() + error: "Ran out of RGBA Yuv422 values! (this should not happen, please file an issue: l1npengtul/nokhwa)".to_string() }) } } @@ -1596,9 +1414,9 @@ pub fn buf_yuyv422_to_rgb(data: &[u8], dest: &mut [u8], rgba: bool) -> Result<() Some(v) => v, None => { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::YUYV, + src: FrameFormat::Yuv422, destination: "RGB888".to_string(), - error: "Ran out of RGB YUYV values! (this should not happen, please file an issue: l1npengtul/nokhwa)".to_string() + error: "Ran out of RGB Yuv422 values! (this should not happen, please file an issue: l1npengtul/nokhwa)".to_string() }) } } @@ -1637,7 +1455,7 @@ pub fn yuyv444_to_rgba(y: i32, u: i32, v: i32) -> [u8; 4] { [r, g, b, 255] } -/// Converts a YUYV 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) +/// Converts a Yuv422 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) /// # Errors /// This may error when the data stream size is wrong. #[inline] @@ -1655,7 +1473,7 @@ pub fn nv12_to_rgb( // this depresses me // like, everytime i open this codebase all the life is sucked out of me // i hate it -/// Converts a YUYV 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream and outputs it into a destination buffer. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) +/// Converts a Yuv422 4:2:0 bi-planar (NV12) datastream to a RGB888 Stream and outputs it into a destination buffer. [For further reading](https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB) /// # Errors /// This may error when the data stream size is wrong. #[allow(clippy::similar_names)] @@ -1668,7 +1486,7 @@ pub fn buf_nv12_to_rgb( ) -> Result<(), NokhwaError> { if resolution.width() % 2 != 0 || resolution.height() % 2 != 0 { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::NV12, + src: FrameFormat::Nv12, destination: "RGB".to_string(), error: "bad resolution".to_string(), }); @@ -1676,7 +1494,7 @@ pub fn buf_nv12_to_rgb( if data.len() != ((resolution.width() * resolution.height() * 3) / 2) as usize { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::NV12, + src: FrameFormat::Nv12, destination: "RGB".to_string(), error: "bad input buffer size".to_string(), }); @@ -1686,7 +1504,7 @@ pub fn buf_nv12_to_rgb( if out.len() != (pxsize * resolution.width() * resolution.height()) as usize { return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::NV12, + src: FrameFormat::Nv12, destination: "RGB".to_string(), error: "bad output buffer size".to_string(), });