new frameformat, new filter, new request

This commit is contained in:
l1npengtul
2023-02-13 21:52:05 +09:00
parent d737d388c6
commit 271d64a4a8
6 changed files with 157 additions and 401 deletions
+3 -3
View File
@@ -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
+5 -6
View File
@@ -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
}
+25 -80
View File
@@ -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<T> IntoPlatformSpecificMany for T
where
T: AsRef<(ApiBackend, u128)>,
{
fn into_psm(self) -> Vec<(ApiBackend, u128)> {
vec![*self.as_ref()]
}
}
impl<T> IntoPlatformSpecificMany for T
where
T: AsRef<[(ApiBackend, u128)]>,
{
fn into_psm(self) -> Vec<(ApiBackend, u128)> {
self.as_ref().iter().collect()
}
}
impl<T> IntoPlatformSpecificMany for BTreeMap<ApiBackend, T>
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<T> IntoPlatformSpecificMany for HashMap<ApiBackend, T>
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<T> IntoPlatformSpecificMany for &T
where
T: IntoPlatformSpecificMany + Clone,
{
fn into_psm(self) -> Vec<(ApiBackend, u128)> {
self.clone().into_psm()
}
}
impl<T> IntoPlatformSpecificMany for &mut T
where
T: IntoPlatformSpecificMany + Clone,
{
fn into_psm(self) -> Vec<(ApiBackend, u128)> {
self.clone().into_psm()
}
}
pub struct FormatFulfill {}
+43 -44
View File
@@ -1,6 +1,26 @@
use crate::error::NokhwaError;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
/*
* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / 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 <l1npengtul@protonmail.com> / 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<T: Pixel, E: Error>: Send + Sync {
const NAME: &'static str;
const PRIMARY: &'static [FrameFormat];
const PLATFORM_ACCEPTABLE: &'static [(ApiBackend, &'static [u128])];
type Primitive: Primitive;
type Container: Deref<Target = [T::Subpixel]>;
type Container: Deref<Target = [Self::Primitive]>;
}
pub trait RgbDecoder: FormatDecoders {
fn decode_rgb(&self, buffer: &Buffer) -> ImageBuffer<Rgb<Self::Primitive>, Self::Container>;
}
pub trait RgbADecoder: FormatDecoders {
fn decode_rgba(&self, buffer: &Buffer) -> ImageBuffer<Rgba<Self::Primitive>, Self::Container>;
}
pub trait LumaDecoder: FormatDecoders {
fn decode_luma(&self, buffer: &Buffer) -> ImageBuffer<Luma<Self::Primitive>, Self::Container>;
}
pub trait LumaADecoder: FormatDecoders {
fn decode_luma_a(
&self,
buffer: &Buffer,
) -> ImageBuffer<LumaA<Self::Primitive>, Self::Container>;
fn decode(&self, buffer: &Buffer) -> Result<ImageBuffer<T, Self::Container>, E>;
}
// TODO: Wgpu Decoder
// TODO: OpenCV Mat Decoder
+9 -14
View File
@@ -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<CameraFormat, NokhwaError>;
/// Initialize the camera, preparing it for use, with a format that fits the supplied [`FormatFilter`].
fn init_with_format(&mut self, format: FormatFilter) -> Result<CameraFormat, NokhwaError>;
/// 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<HashMap<Resolution, Vec<u32>>, 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<Vec<FrameFormat>, 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<Vec<SourceFrameFormat>, 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;
+72 -254
View File
@@ -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<Decoder: FormatDecoders>(
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<CameraFormat> {
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::<Vec<CameraFormat>>();
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::<Vec<CameraFormat>>();
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::<Vec<CameraFormat>>();
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::<Vec<CameraFormat>>();
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::<Vec<CameraFormat>>();
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::<Vec<(i32, Resolution)>>();
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::<Vec<u32>>();
// 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::<Vec<(u32, u32)>>();
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<u8>` 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<mozjpeg::decompress::DecompressStarted, NokhwaError> {
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<u8>` 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<Vec<u8>, 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<Vec<u8>> = 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<Vec<u8>, 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<Vec<u8>, 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<Vec<u8>, NokhwaError> {
/// Same as [`yuyv422_to_rgb`] but with a destination buffer instead of a return `Vec<u8>`
/// # 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(),
});