diff --git a/src/backends/capture/uvc_backend.rs b/src/backends/capture/uvc_backend.rs index 7f94449..adcd64f 100644 --- a/src/backends/capture/uvc_backend.rs +++ b/src/backends/capture/uvc_backend.rs @@ -6,12 +6,11 @@ use std::{ cell::{Cell, RefCell}, collections::HashMap, mem::MaybeUninit, - ops::DerefMut, sync::{atomic::AtomicUsize, Arc}, }; use uvc::{ - ActiveStream, Context, Device, DeviceHandle, DeviceList, Error, FrameFormat as UVCFrameFormat, - StreamFormat, StreamHandle, + ActiveStream, Context, Device, DeviceHandle, FrameFormat as UVCFrameFormat, StreamFormat, + StreamHandle, }; #[cfg(feature = "input_uvc")] @@ -40,23 +39,24 @@ impl From for StreamFormat { /// The backend struct that interfaces with libuvc. /// To see what this does, please see [`CaptureBackendTrait`] /// # Quirks -/// The indexing for this backend is based off of `libuvc`'s device ordering, not the OS. -/// You must call [create()](UVCCaptureDevice::create()) instead `new()`, some methods are auto-generated by the self-referencer and are not meant to be used. -/// The [create()](UVCCaptureDevice::create()) method will open the device twice. -/// Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat) -/// each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format). +/// - The indexing for this backend is based off of `libuvc`'s device ordering, not the OS. +/// - You must call [create()](UVCCaptureDevice::create()) instead `new()`, some methods are auto-generated by the self-referencer and are not meant to be used. +/// - The [create()](UVCCaptureDevice::create()) method will open the device twice. +/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution()), [`set_framerate()`](CaptureBackendTrait::set_framerate()), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat()) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format()). +/// - [`get_frame_raw()`](CaptureBackendTrait::get_frame_raw()) returns the same raw data as [`get_frame()`](CaptureBackendTrait::get_frame()), a.k.a. no custom decoding required, all data is automatically RGB +/// This backend, once stream is open, will constantly collect frames. When you call [`get_frame()`](CaptureBackendTrait::get_frame()) or one of its variants, it will only give you the latest frame. /// # Safety /// This backend requires use of `unsafe` due to the self-referencing structs involved. -#[allow(clippy::too_many_arguments)] +/// If [`open_stream()`](CaptureBackendTrait::open_stream()) and [`get_frame()`](CaptureBackendTrait::get_frame()) are called in the wrong order this may crash the entire program. #[self_referencing(chain_hack)] pub struct UVCCaptureDevice<'a> { - camera_format: Option, + camera_format: CameraFormat, camera_info: CameraInfo, frame_receiver: Box>>, frame_sender: Box>>, - context: Box>, stream_handle_init: Cell, active_stream_init: Cell, + context: Box>, #[borrows(context)] #[not_covariant] device: Box>, @@ -73,11 +73,12 @@ pub struct UVCCaptureDevice<'a> { impl<'a> UVCCaptureDevice<'a> { /// Creates a UVC Camera device with optional [`CameraFormat`]. + /// If `camera_format` is `None`, it will be spawned with with 640x480@15 FPS, MJPEG [`CameraFormat`] default. /// # Panics /// This operation may panic! If the UVC Context fails to retrieve the device from the gotten IDs, this operation will panic. /// # Errors /// This may error when the `libuvc` backend fails to retreive the device or its data. - pub fn create(index: usize, camera_format: Option) -> Result { + pub fn create(index: usize, cam_fmt: Option) -> Result { let context = match Context::new() { Ok(ctx) => Box::new(ctx), Err(why) => return Err(NokhwaError::CouldntOpenDevice(why.to_string())), @@ -147,6 +148,11 @@ impl<'a> UVCCaptureDevice<'a> { ) }; + let camera_format = match cam_fmt { + Some(cfmt) => cfmt, + None => CameraFormat::default(), + }; + Ok(UVCCaptureDeviceBuilder { camera_format, camera_info, @@ -197,16 +203,8 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> { self.borrow_camera_info().clone() } - fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError> { - let camera_format = CameraFormat::default(); - - self.with_mut(|fields| {}); - - Ok(()) - } - - fn get_camera_format(&self) -> Option { - todo!() + fn get_camera_format(&self) -> CameraFormat { + *self.borrow_camera_format() } fn get_compatible_list_by_resolution( @@ -221,36 +219,51 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> { } fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> { - let ret: Result<(), NokhwaError> = self.with_mut(|fields| { - *fields.camera_format = Some(new_fmt); - let is_streamh_some = { fields.stream_handle_init.get() }; - if is_streamh_some { - // TODO - } + let prev_fmt = *self.borrow_camera_format(); - Ok(()) + self.with_camera_format_mut(|cfmt| { + *cfmt = new_fmt; }); + + let is_streamh_some = self.borrow_stream_handle_init().get(); + + if is_streamh_some { + return match self.open_stream() { + Ok(_) => Ok(()), + Err(why) => { + // revert + self.with_camera_format_mut(|cfmt| { + *cfmt = prev_fmt; + }); + Err(NokhwaError::CouldntSetProperty { + property: "CameraFormat".to_string(), + value: new_fmt.to_string(), + error: why.to_string(), + }) + } + }; + } Ok(()) } - fn get_resolution(&self) -> Option { - self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::resoltuion)) + fn get_resolution(&self) -> Resolution { + self.borrow_camera_format().resoltuion() } fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> { todo!() } - fn get_framerate(&self) -> Option { - self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::framerate)) + fn get_framerate(&self) -> u32 { + self.borrow_camera_format().framerate() } fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> { todo!() } - fn get_frameformat(&self) -> Option { - self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::format)) + fn get_frameformat(&self) -> FrameFormat { + self.borrow_camera_format().format() } fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> { @@ -259,42 +272,20 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> { fn open_stream(&mut self) -> Result<(), NokhwaError> { let ret: Result<(), NokhwaError> = self.with_mut(|fields| { - let stream_format: StreamFormat = match fields.camera_format { - Some(fmt) => { - let cameraformat: CameraFormat = *fmt; - cameraformat.into() - } - None => { - return Err(NokhwaError::CouldntOpenStream( - "Please initialise the CameraFormat first!".to_string(), - )) - } - }; + let stream_format: StreamFormat = CameraFormat::into(*fields.camera_format); // first, drop the existing stream by setting it to None { - match fields.active_stream.try_borrow_mut() { - Ok(raw_astream) => { - if fields.active_stream_init.get() { - std::mem::drop(raw_astream.assume_init()); - std::mem::drop(raw_astream); - *raw_astream = MaybeUninit::uninit(); - fields.active_stream_init.set(false); - } - } - Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())), + if fields.active_stream_init.get() { + let innard_value = fields.active_stream.replace(MaybeUninit::uninit()); + unsafe { std::mem::drop(innard_value.assume_init()) }; + fields.active_stream_init.set(false); } - match fields.stream_handle.try_borrow_mut() { - Ok(mut raw_streamh) => { - if fields.stream_handle_init.get() { - std::mem::drop(raw_streamh.assume_init()); - std::mem::drop(raw_streamh); - *raw_streamh = MaybeUninit::uninit(); - fields.stream_handle_init.set(false); - } - } - Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())), + if fields.stream_handle_init.get() { + let innard_value = fields.stream_handle.replace(MaybeUninit::uninit()); + unsafe { std::mem::drop(innard_value.assume_init()) }; + fields.stream_handle_init.set(false); } } // second, set the stream handle according to the streamformat @@ -322,27 +313,103 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> { // finally, get the active stream let counter = Arc::new(AtomicUsize::new(0)); let frame_sender: Sender> = *(self.with_frame_sender(|send| send)).clone(); - let mut streamh = unsafe {}; - // let active_stream = streamh.start_stream(|frame, _cnt| {}, counter); + let streamh = unsafe { + let raw_ptr = + (*fields.stream_handle.borrow_mut()).as_ptr() as *mut MaybeUninit; + let assume_inited: *mut MaybeUninit> = + raw_ptr.cast::>(); + &mut *assume_inited + }; + let streamh_init = unsafe { + match streamh.as_mut_ptr().as_mut() { + Some(sth) => sth, + None => { + return Err(NokhwaError::CouldntOpenStream( + "Failed to get mutable raw pointer to stream handle!".to_string(), + )) + } + } + }; + let active_stream = match streamh_init.start_stream( + move |frame, _count| { + let vec_frame: Vec = frame.to_rgb().unwrap().to_bytes().to_vec(); + if frame_sender.send(vec_frame).is_err() { + // do nothing + } + }, + counter, + ) { + Ok(active) => active, + Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())), + }; + *fields.active_stream.borrow_mut() = MaybeUninit::new(active_stream); Ok(()) }); + + if ret_2.is_err() { + return ret_2; + } + Ok(()) } fn is_stream_open(&self) -> bool { - todo!() + self.with_active_stream_init(Cell::get) } fn get_frame(&mut self) -> Result, Vec>, NokhwaError> { - todo!() + let data = match self.get_frame_raw() { + Ok(d) => d, + Err(why) => return Err(why), + }; + + let resolution: Resolution = self.borrow_camera_format().resoltuion(); + + let imagebuf: ImageBuffer, Vec> = + match ImageBuffer::from_vec(resolution.width(), resolution.height(), data) { + Some(img) => img, + None => { + return Err(NokhwaError::CouldntCaptureFrame( + "ImageBuffer too small! This is probably a bug, please report it!" + .to_string(), + )) + } + }; + + Ok(imagebuf) } fn get_frame_raw(&mut self) -> Result, NokhwaError> { - todo!() + // assertions + if !self.borrow_active_stream_init().get() { + return Err(NokhwaError::CouldntCaptureFrame( + "Please call `open_stream()` first!".to_string(), + )); + } + + let f_recv = self.borrow_frame_receiver(); + let messages_iter = f_recv.drain(); + match messages_iter.last() { + Some(msg) => Ok(msg), + None => Err(NokhwaError::CouldntCaptureFrame("Too fast!".to_string())), + } } fn stop_stream(&mut self) -> Result<(), NokhwaError> { - todo!() + self.with(|fields| { + if fields.active_stream_init.get() { + let innard_value = fields.active_stream.replace(MaybeUninit::uninit()); + unsafe { std::mem::drop(innard_value.assume_init()) }; + fields.active_stream_init.set(false); + } + + if fields.stream_handle_init.get() { + let innard_value = fields.stream_handle.replace(MaybeUninit::uninit()); + unsafe { std::mem::drop(innard_value.assume_init()) }; + fields.stream_handle_init.set(false); + } + }); + Ok(()) } } diff --git a/src/backends/capture/v4l2.rs b/src/backends/capture/v4l2.rs index 28630ed..1fa636a 100644 --- a/src/backends/capture/v4l2.rs +++ b/src/backends/capture/v4l2.rs @@ -30,10 +30,9 @@ impl From for Format { /// The backend struct that interfaces with V4L2. /// To see what this does, please see [`CaptureBackendTrait`] /// # Quirks -/// Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat) -/// each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format). +/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format). pub struct V4LCaptureDevice<'a> { - camera_format: Option, + camera_format: CameraFormat, camera_info: CameraInfo, device: Device, stream_handle: Option>, @@ -41,9 +40,11 @@ pub struct V4LCaptureDevice<'a> { impl<'a> V4LCaptureDevice<'a> { /// Creates a new capture device using the V4L2 backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery. + /// + /// If `camera_format` is `None`, it will be spawned with with 640x480@15 FPS, MJPEG [`CameraFormat`] default. /// # Errors /// This function will error if the camera is currently busy or if V4L2 can't read device information. - pub fn new(index: usize, camera_format: Option) -> Result { + pub fn new(index: usize, cam_fmt: Option) -> Result { let device = match Device::new(index) { Ok(dev) => dev, Err(why) => { @@ -64,6 +65,11 @@ impl<'a> V4LCaptureDevice<'a> { } }; + let camera_format = match cam_fmt { + Some(cfmt) => cfmt, + None => CameraFormat::default(), + }; + Ok(V4LCaptureDevice { camera_format, camera_info, @@ -92,20 +98,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> { self.camera_info.clone() } - #[allow(clippy::option_if_let_else)] - fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError> { - match self.camera_format { - Some(_) => { - if overwrite { - return self.set_camera_format(CameraFormat::default()); - } - Ok(()) - } - None => self.set_camera_format(CameraFormat::default()), - } - } - - fn get_camera_format(&self) -> Option { + fn get_camera_format(&self) -> CameraFormat { self.camera_format } @@ -242,60 +235,41 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> { } }; } - self.camera_format = Some(new_fmt); + self.camera_format = new_fmt; Ok(()) } - fn get_resolution(&self) -> Option { - self.camera_format.map(|fmt| fmt.resoltuion()) + fn get_resolution(&self) -> Resolution { + self.camera_format.resolution() } #[allow(clippy::option_if_let_else)] fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> { - if let Some(fmt) = self.camera_format { - let mut new_fmt = fmt; - new_fmt.set_resolution(new_res); - self.set_camera_format(new_fmt) - } else { - self.camera_format = Some(CameraFormat::new(new_res, FrameFormat::MJPEG, 0)); - Ok(()) - } + let mut new_fmt = self.camera_format; + new_fmt.set_resolution(new_res); + self.set_camera_format(new_fmt) } - fn get_framerate(&self) -> Option { - self.camera_format.map(|fmt| fmt.framerate()) + fn get_framerate(&self) -> u32 { + self.camera_format.framerate() } #[allow(clippy::option_if_let_else)] fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> { - if let Some(fmt) = self.camera_format { - let mut new_fmt = fmt; - new_fmt.set_framerate(new_fps); - self.set_camera_format(new_fmt) - } else { - self.camera_format = Some(CameraFormat::new( - Resolution::new(0, 0), - FrameFormat::MJPEG, - new_fps, - )); - Ok(()) - } + let mut new_fmt = self.camera_format; + new_fmt.set_framerate(new_fps); + self.set_camera_format(new_fmt) } - fn get_frameformat(&self) -> Option { - self.camera_format.map(|fmt| fmt.format()) + fn get_frameformat(&self) -> FrameFormat { + self.camera_format.format() } #[allow(clippy::option_if_let_else)] fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> { - if let Some(fmt) = self.camera_format { - let mut new_fmt = fmt; - new_fmt.set_format(fourcc); - self.set_camera_format(new_fmt) - } else { - self.camera_format = Some(CameraFormat::new(Resolution::new(0, 0), fourcc, 0)); - Ok(()) - } + let mut new_fmt = self.camera_format; + new_fmt.set_format(fourcc); + self.set_camera_format(new_fmt) } fn open_stream(&mut self) -> Result<(), NokhwaError> { @@ -313,15 +287,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> { fn get_frame(&mut self) -> Result, Vec>, NokhwaError> { let raw_frame = self.get_frame_raw()?; - let cam_fmt = match self.camera_format { - Some(fmt) => fmt, - None => { - return Err(NokhwaError::CouldntCaptureFrame( - "CameraFormat isn't initialized! This is probably a bug, please report it" - .to_string(), - )); - } - }; + let cam_fmt = self.camera_format; let conv = match cam_fmt.format() { FrameFormat::MJPEG => mjpeg_to_rgb888(&raw_frame)?, FrameFormat::YUYV => yuyv422_to_rgb888(&raw_frame)?, diff --git a/src/camera_traits.rs b/src/camera_traits.rs index 24fbd85..06e22af 100644 --- a/src/camera_traits.rs +++ b/src/camera_traits.rs @@ -9,50 +9,47 @@ use crate::{ /// 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 program will halt while it waits for it to become availible. +/// +/// **Note**: +/// - Backends, if not provided with a camera format, will be spawned with 640x480@15 FPS, MJPEG [`CameraFormat`]. +/// - Behaviour can differ from backend to backend. While the [`Capture`] struct abstracts most of this away, if you plan to use the raw backend structs please read the `Quirks` section of each backend. pub trait CaptureBackendTrait { /// Gets the camera information such as Name and Index as a [`CameraInfo`]. fn get_info(&self) -> CameraInfo; - /// Assigns a sensible default to the backend's [`CameraFormat`]. Usually 640x480 @ 15 FPS + MJPEG. - /// If there is already a value assigned to the [`CameraFormat`], it will only be overwritten if `overwrite` is set to `true`. - /// If false, this function will do nothing (NO-OP). - /// This will reset the current stream if used while stream is opened. - /// # Errors - /// If you started the stream and the camera rejects the new camera format, this will return an error. - fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError>; - /// Gets the current [`CameraFormat`]. Will return none if no format has been set yet. - fn get_camera_format(&self) -> Option; + /// Gets the current [`CameraFormat`]. + fn get_camera_format(&self) -> CameraFormat; /// A hashmap of [`Resolution`]s mapped to framerates /// # 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 (see: [`NokhwaError::UnsupportedOperation`]). + /// 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 ([`NokhwaError::UnsupportedOperation`]). fn get_compatible_list_by_resolution( &self, fourcc: FrameFormat, ) -> Result>, NokhwaError>; /// Gets the supported camera formats. /// # Errors - /// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a NO-OP [`NokhwaError::UnsupportedOperation`] + /// 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 ([`NokhwaError::UnsupportedOperation`]). fn get_resolution_list(&self, fourcc: FrameFormat) -> Result, NokhwaError>; /// Will set the current [`CameraFormat`] /// This will reset the current stream if used while stream is opened. /// # Errors /// If you started the stream and the camera rejects the new camera format, this will return an error. fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError>; - /// Gets the current camera resolution (See: [`Resolution`]). Will return none if no resolution has been set yet. - fn get_resolution(&self) -> Option; + /// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]). + fn get_resolution(&self) -> Resolution; /// Will set the current [`Resolution`] /// This will reset the current stream if used while stream is opened. /// # Errors /// If you started the stream and the camera rejects the new resolution, this will return an error. fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError>; - /// Gets the current camera framerate. Will return none if no framerate has been set yet. - fn get_framerate(&self) -> Option; + /// Gets the current camera framerate (See: [`CameraFormat`]). + fn get_framerate(&self) -> u32; /// Will set the current framerate /// This will reset the current stream if used while stream is opened. /// # Errors /// If you started the stream and the camera rejects the new framerate, this will return an error. fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError>; - /// Gets the current camera's frame format (See: [`FrameFormat`]). Will return none if no frame format has been set yet. - fn get_frameformat(&self) -> Option; + /// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]). + fn get_frameformat(&self) -> FrameFormat; /// Will set the current [`FrameFormat`] /// This will reset the current stream if used while stream is opened. /// # Errors diff --git a/src/utils.rs b/src/utils.rs index ba7b648..5bbb5e4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,9 +1,10 @@ use crate::NokhwaError; use mozjpeg::Decompress; +use std::fmt::Formatter; use std::{cmp::Ordering, convert::TryFrom, fmt::Display, slice::from_raw_parts}; /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`
-/// YUYV is a mathmatical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr)
+/// YUYV is a mathematical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr)
/// MJPEG is a motion-jpeg compressed frame, it allows for high frame rates. #[derive(Copy, Clone, Debug, PartialEq, Hash)] pub enum FrameFormat { @@ -84,7 +85,7 @@ impl Ord for Resolution { } } -/// This is a conveinence struct that holds all information about the format of a webcam stream. +/// This is a convenience struct that holds all information about the format of a webcam stream. /// It consists of a [`Resolution`], [`FrameFormat`], and a framerate(u8). #[derive(Copy, Clone, Debug, Hash, PartialEq)] pub struct CameraFormat { @@ -116,7 +117,7 @@ impl CameraFormat { } /// Get the resolution of the current [`CameraFormat`] - pub fn resoltuion(&self) -> Resolution { + pub fn resolution(&self) -> Resolution { self.resolution } @@ -166,6 +167,16 @@ impl Default for CameraFormat { } } +impl Display for CameraFormat { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}@{}FPS, {} Format", + self.resolution, self.framerate, self.format + ) + } +} + /// Information about a Camera e.g. its name. /// `description` amd `misc` may contain backend-specific information. /// `index` is a camera's index given to it by (usually) the OS usually in the order it is known to the system. @@ -241,6 +252,16 @@ impl Ord for CameraInfo { } } +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 + ) + } +} + /// The list of known capture backends to the library.
/// - AUTO is special - it tells the Camera struct to automatically choose a backend most suited for the current platform. /// - V4L2 - `Video4Linux2`, a linux specific backend. @@ -267,7 +288,7 @@ impl Display for CaptureAPIBackend { /// 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 decompresser, this will error. +/// If `mozjpeg` fails to read scanlines or setup the decompressor, this will error. /// # Safety /// This function uses `unsafe`. The caller must ensure that: /// - The input data is of the right size, does not exceed bounds, and/or the final size matches with the initial size.