use crate::NokhwaError;
use mozjpeg::Decompress;
use std::fmt::Formatter;
use std::{cmp::Ordering, convert::TryFrom, fmt::Display, slice::from_raw_parts};
#[derive(Copy, Clone, Debug, PartialEq, Hash, PartialOrd, Ord, Eq)]
pub enum FrameFormat {
MJPEG,
YUYV,
}
impl Display for FrameFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrameFormat::MJPEG => {
write!(f, "MJPEG")
}
FrameFormat::YUYV => {
write!(f, "YUYV")
}
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Resolution {
pub width_x: u32,
pub height_y: u32,
}
impl Resolution {
pub fn new(x: u32, y: u32) -> Self {
Resolution {
width_x: x,
height_y: y,
}
}
pub fn width(self) -> u32 {
self.width_x
}
pub fn height(self) -> u32 {
self.height_y
}
pub fn x(self) -> u32 {
self.width_x
}
pub fn y(self) -> u32 {
self.height_y
}
}
impl Display for Resolution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}x{}", self.x(), self.y())
}
}
impl PartialOrd for Resolution {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Resolution {
fn cmp(&self, other: &Self) -> Ordering {
match self.x().cmp(&other.x()) {
Ordering::Less => Ordering::Less,
Ordering::Equal => self.y().cmp(&other.y()),
Ordering::Greater => Ordering::Greater,
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
pub struct CameraFormat {
resolution: Resolution,
format: FrameFormat,
framerate: u32,
}
impl CameraFormat {
pub fn new(resolution: Resolution, format: FrameFormat, framerate: u32) -> Self {
CameraFormat {
resolution,
format,
framerate,
}
}
pub fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: u32) -> Self {
CameraFormat {
resolution: Resolution {
width_x: res_x,
height_y: res_y,
},
format,
framerate: fps,
}
}
pub fn resolution(&self) -> Resolution {
self.resolution
}
pub fn width(&self) -> u32 {
self.resolution.width()
}
pub fn height(&self) -> u32 {
self.resolution.height()
}
pub fn set_resolution(&mut self, resolution: Resolution) {
self.resolution = resolution;
}
pub fn framerate(&self) -> u32 {
self.framerate
}
pub fn set_framerate(&mut self, framerate: u32) {
self.framerate = framerate;
}
pub fn format(&self) -> FrameFormat {
self.format
}
pub fn set_format(&mut self, format: FrameFormat) {
self.format = format;
}
}
impl Default for CameraFormat {
fn default() -> Self {
CameraFormat {
resolution: Resolution::new(640, 480),
format: FrameFormat::MJPEG,
framerate: 15,
}
}
}
impl Display for CameraFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}@{}FPS, {} Format",
self.resolution, self.framerate, self.format
)
}
}
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
pub struct CameraInfo {
human_name: String,
description: String,
misc: String,
index: usize,
}
impl CameraInfo {
pub fn new(human_name: String, description: String, misc: String, index: usize) -> Self {
CameraInfo {
human_name,
description,
misc,
index,
}
}
pub fn human_name(&self) -> &String {
&self.human_name
}
pub fn set_human_name(&mut self, human_name: String) {
self.human_name = human_name;
}
pub fn description(&self) -> &String {
&self.description
}
pub fn set_description(&mut self, description: String) {
self.description = description;
}
pub fn misc(&self) -> &String {
&self.misc
}
pub fn set_misc(&mut self, misc: String) {
self.misc = misc;
}
pub fn index(&self) -> &usize {
&self.index
}
pub fn set_index(&mut self, index: usize) {
self.index = index;
}
}
impl PartialOrd for CameraInfo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CameraInfo {
fn cmp(&self, other: &Self) -> Ordering {
self.index.cmp(&other.index)
}
}
impl Display for CameraInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Name: {}, Description: {}, Extra: {}, Index: {}",
self.human_name, self.description, self.misc, self.index
)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CaptureAPIBackend {
Auto,
AVFoundation,
Video4Linux,
UniversalVideoClass,
Windows,
OpenCv,
Ffmpeg,
GStreamer,
}
impl Display for CaptureAPIBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let self_str = format!("{:?}", self);
write!(f, "{}", self_str)
}
}
pub fn mjpeg_to_rgb888(data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
let mut mozjpeg_decomp = match Decompress::new_mem(data) {
Ok(decomp) => match decomp.rgb() {
Ok(decompresser) => decompresser,
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::MJPEG,
destination: "RGB888".to_string(),
error: why.to_string(),
})
}
},
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::MJPEG,
destination: "RGB888".to_string(),
error: why.to_string(),
})
}
};
let decompressed = match mozjpeg_decomp.read_scanlines::<[u8; 3]>() {
Some(pixels) => pixels,
None => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::MJPEG,
destination: "RGB888".to_string(),
error: "Failed to get read readlines into RGB888 pixels!".to_string(),
})
}
};
Ok(unsafe { from_raw_parts(decompressed.as_ptr().cast(), decompressed.len() * 3) }.to_vec())
}
pub fn yuyv422_to_rgb888(data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
let mut rgb_vec: Vec<u8> = vec![];
if data.len() % 4 == 0 {
for px_idx in (0..data.len()).step_by(4) {
let y1 = match data.get(px_idx) {
Some(px) => match i32::try_from(*px) {
Ok(i) => i,
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame{ src: FrameFormat::YUYV, destination: "RGB888".to_string(), error: format!("Failed to convert byte at {} to a i32 because {}, This shouldn't happen!", px_idx, why.to_string()) });
}
},
None => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::YUYV,
destination: "RGB888".to_string(),
error: format!(
"Failed to get bytes at {}, this is probably a bug, please report!",
px_idx
),
});
}
};
let u = match data.get(px_idx + 1) {
Some(px) => match i32::try_from(*px) {
Ok(i) => i,
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame{ src: FrameFormat::YUYV, destination: "RGB888".to_string(), error: format!("Failed to convert byte at {} to a i32 because {}, This shouldn't happen!", px_idx+1, why.to_string()) });
}
},
None => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::YUYV,
destination: "RGB888".to_string(),
error: format!(
"Failed to get bytes at {}, this is probably a bug, please report!",
px_idx + 1
),
});
}
};
let y2 = match data.get(px_idx + 2) {
Some(px) => match i32::try_from(*px) {
Ok(i) => i,
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame{ src: FrameFormat::YUYV, destination: "RGB888".to_string(), error: format!("Failed to convert byte at {} to a i32 because {}, This shouldn't happen!", px_idx+2, why.to_string()) });
}
},
None => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::YUYV,
destination: "RGB888".to_string(),
error: format!(
"Failed to get bytes at {}, this is probably a bug, please report!",
px_idx + 2
),
});
}
};
let v = match data.get(px_idx + 3) {
Some(px) => match i32::try_from(*px) {
Ok(i) => i,
Err(why) => {
return Err(NokhwaError::CouldntDecompressFrame{ src: FrameFormat::YUYV, destination: "RGB888".to_string(), error: format!("Failed to convert byte at {} to a i32 because {}, This shouldn't happen!", px_idx+3, why.to_string()) });
}
},
None => {
return Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::YUYV,
destination: "RGB888".to_string(),
error: format!(
"Failed to get bytes at {}, this is probably a bug, please report!",
px_idx + 3
),
});
}
};
let pixel1 = yuyv444_to_rgb888(y1, u, v);
let pixel2 = yuyv444_to_rgb888(y2, u, v);
rgb_vec.append(&mut pixel1.to_vec());
rgb_vec.append(&mut pixel2.to_vec());
}
Ok(rgb_vec)
} else {
Err(NokhwaError::CouldntDecompressFrame {
src: FrameFormat::YUYV,
destination: "RGB888".to_string(),
error: "Assertion failure, the YUV stream isn't 4:2:2! (wrong number of bytes)"
.to_string(),
})
}
}
#[allow(clippy::many_single_char_names)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
pub fn yuyv444_to_rgb888(y: i32, u: i32, v: i32) -> [u8; 3] {
let c298 = (y - 16) * 298;
let d = u - 128;
let e = v - 128;
let r = ((c298 + 409 * e + 128) >> 8).clamp(0, 255) as u8;
let g = ((c298 - 100 * d - 208 * e + 128) >> 8).clamp(0, 255) as u8;
let b = ((c298 + 516 * d + 128) >> 8).clamp(0, 255) as u8;
[r, g, b]
}
#[derive(Clone, Debug, PartialEq)]
pub enum CameraIndexType {
Index(u32),
IPCamera(String),
}
impl Display for CameraIndexType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CameraIndexType::Index(idx) => {
write!(f, "{}", idx)
}
CameraIndexType::IPCamera(ip) => {
write!(f, "{}", ip)
}
}
}
}