fix core compile errors, yuv and mjpeg decoder implementation TODO write tests

This commit is contained in:
l1npengtul
2025-07-10 18:21:40 +09:00
parent a2f458c331
commit f69aba1c0f
22 changed files with 1457 additions and 652 deletions
Generated
+6 -6
View File
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1750215678,
"narHash": "sha256-Rc/ytpamXRf6z8UA2SGa4aaWxUXRbX2MAWIu2C8M+ok=",
"lastModified": 1751498133,
"narHash": "sha256-QWJ+NQbMU+NcU2xiyo7SNox1fAuwksGlQhpzBl76g1I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5395fb3ab3f97b9b7abca147249fa2e8ed27b192",
"rev": "d55716bb59b91ae9d1ced4b1ccdea7a442ecbfdb",
"type": "github"
},
"original": {
@@ -62,11 +62,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1750387093,
"narHash": "sha256-MgL1+yNVcSD6OlzSmKt5GS4RmAQnNCjckjgPC1hmMPg=",
"lastModified": 1751510438,
"narHash": "sha256-m8PjOoyyCR4nhqtHEBP1tB/jF+gJYYguSZmUmVTEAQE=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "517e9871d182346b53bb7f23fed00810c14db396",
"rev": "7f415261f298656f8164bd636c0dc05af4e95b6b",
"type": "github"
},
"original": {
+3 -1
View File
@@ -12,7 +12,7 @@ repository = "https://github.com/l1npengtul/nokhwa"
[features]
default = []
serialize = ["serde"]
serialize = ["serde", "ordered-float/serde"]
wgpu = ["wgpu-types"]
opencv-mat = ["opencv", "opencv/clang-runtime"]
docs-features = ["serialize", "wgpu-types"]
@@ -29,6 +29,8 @@ typed-builder = "0.21"
compact_str = "0.9"
bytemuck = "1.23"
smallmap = "1.4"
constcat = "0.6"
paste = "1.0"
[dependencies.num-rational]
version = "0.4"
+8 -6
View File
@@ -23,11 +23,11 @@ pub trait Setting {
/// Will error on
fn set_format(&mut self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
fn control_ids(&self) -> Keys<ControlId, ControlDescription>;
fn control_ids(&self) -> Keys<'_, ControlId, ControlDescription>;
fn control_descriptions(&self) -> Values<ControlId, ControlDescription>;
fn control_descriptions(&self) -> Values<'_, ControlId, ControlDescription>;
fn control_values(&self) -> Values<ControlId, ControlValue>;
fn control_values(&self) -> Values<'_, ControlId, ControlValue>;
fn control_value(&self, id: &ControlId) -> Option<&ControlValue>;
@@ -44,6 +44,7 @@ pub trait Setting {
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncSetting {
async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
@@ -68,7 +69,7 @@ pub trait Capture {
fn open_stream(
&mut self,
stream_configuration: Option<StreamConfiguration>,
) -> Result<Arc<StreamHandle>, NokhwaError>;
) -> Result<Arc<StreamHandle<'_>>, NokhwaError>;
// Implementations MUST be multi-close tolerant.
/// # Errors
@@ -77,11 +78,12 @@ pub trait Capture {
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncStream {
async fn open_stream_async(
async fn open_stream_async<'a>(
&mut self,
stream_configuration: Option<StreamConfiguration>,
) -> Result<StreamHandle, NokhwaError>;
) -> Result<StreamHandle<'a>, NokhwaError>;
async fn close_stream_async(&mut self) -> Result<(), NokhwaError>;
}
+7 -7
View File
@@ -8,7 +8,7 @@ pub trait Codec {
type Input<'a>;
type Output;
type Output<'a>;
type WrittenMeta: Clone + Debug;
@@ -28,7 +28,7 @@ pub trait Codec {
fn receive_decoded_item(
&mut self,
writing_output: &mut Self::Output,
writing_output: &mut Self::Output<'_>,
) -> Result<Self::WrittenMeta, NokhwaError>;
fn preferred_buffer_min_size(
@@ -40,15 +40,15 @@ pub trait Codec {
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait CodecAsync: Codec {
async fn allowed_formats_async(&self) -> Result<&[FrameFormat], NokhwaError> {
async fn allowed_formats_async<'a>(&'a self) -> Result<&'a [FrameFormat], NokhwaError> {
self.allowed_formats()
}
async fn set_format_async(&self, format: CameraFormat) -> Result<(), NokhwaError>;
async fn config_async(&self) -> &Self::Config {
async fn config_async<'a>(&'a self) -> &'a Self::Config {
self.config()
}
@@ -56,11 +56,11 @@ pub trait CodecAsync: Codec {
self.set_config(config)
}
fn send_item_async(&mut self, input: &Self::Input) -> Result<(), NokhwaError>;
fn send_item_async(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError>;
fn receive_decoded_item_async(
&mut self,
writing_output: &mut Self::Output,
writing_output: &mut Self::Output<'_>,
) -> Result<Option<usize>, NokhwaError>;
async fn deinitialize_async(&mut self) -> Result<(), NokhwaError> {
+45 -25
View File
@@ -61,17 +61,18 @@ pub struct Controls {
impl Controls {
/// INVARIANTS: All `ControlId` in `device_values` MUST exist in `device_controls`
#[must_use] pub fn new(
#[must_use]
pub fn new(
device_controls: HashMap<ControlId, ControlDescription>,
device_values: HashMap<ControlId, ControlValue>,
) -> Option<Self> {
for (id, value) in &device_values {
if let Some(description) = device_controls.get(id) {
if !description.validate(value) {
if let Some(description) = device_controls.get(id)
&& !description.validate(value)
{
return None;
}
}
}
Some(Self {
descriptions: device_controls,
@@ -79,11 +80,13 @@ impl Controls {
})
}
#[must_use] pub fn empty() -> Self {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use] pub fn unchecked_new(
#[must_use]
pub fn unchecked_new(
device_controls: HashMap<ControlId, ControlDescription>,
device_values: HashMap<ControlId, ControlValue>,
) -> Self {
@@ -93,23 +96,28 @@ impl Controls {
}
}
#[must_use] pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
#[must_use]
pub fn description(&self, control_id: &ControlId) -> Option<&ControlDescription> {
self.descriptions.get(control_id)
}
#[must_use] pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
#[must_use]
pub fn value(&self, control_id: &ControlId) -> Option<&ControlValue> {
self.values.get(control_id)
}
#[must_use] pub fn descriptions(&self) -> Values<ControlId, ControlDescription> {
#[must_use]
pub fn descriptions(&self) -> Values<'_, ControlId, ControlDescription> {
self.descriptions.values()
}
#[must_use] pub fn values(&self) -> Values<ControlId, ControlValue> {
#[must_use]
pub fn values(&self) -> Values<'_, ControlId, ControlValue> {
self.values.values()
}
#[must_use] pub fn ids(&self) -> Keys<ControlId, ControlDescription> {
#[must_use]
pub fn ids(&self) -> Keys<'_, ControlId, ControlDescription> {
self.descriptions.keys()
}
@@ -163,16 +171,17 @@ pub struct ControlDescription {
}
impl ControlDescription {
#[must_use] pub fn new(
#[must_use]
pub fn new(
control_flags: HashSet<ControlFlags>,
control_value_descriptor: ControlValueDescriptor,
default_value: Option<ControlValue>,
) -> Option<Self> {
if let Some(default) = &default_value {
if !control_value_descriptor.validate(default) {
if let Some(default) = &default_value
&& !control_value_descriptor.validate(default)
{
return None;
}
}
Some(Self {
flags: control_flags,
@@ -181,7 +190,8 @@ impl ControlDescription {
})
}
#[must_use] pub fn new_unchecked(
#[must_use]
pub fn new_unchecked(
control_flags: HashSet<ControlFlags>,
control_value_descriptor: ControlValueDescriptor,
default_value: Option<ControlValue>,
@@ -193,15 +203,18 @@ impl ControlDescription {
}
}
#[must_use] pub fn flags(&self) -> &HashSet<ControlFlags> {
#[must_use]
pub fn flags(&self) -> &HashSet<ControlFlags> {
&self.flags
}
#[must_use] pub fn descriptor(&self) -> &ControlValueDescriptor {
#[must_use]
pub fn descriptor(&self) -> &ControlValueDescriptor {
&self.descriptor
}
#[must_use] pub fn default_value(&self) -> &Option<ControlValue> {
#[must_use]
pub fn default_value(&self) -> &Option<ControlValue> {
&self.default_value
}
@@ -213,7 +226,8 @@ impl ControlDescription {
self.flags.remove(&flag)
}
#[must_use] pub fn validate(&self, value: &ControlValue) -> bool {
#[must_use]
pub fn validate(&self, value: &ControlValue) -> bool {
self.descriptor.validate(value)
}
}
@@ -261,7 +275,8 @@ pub enum ControlValueDescriptor {
}
impl ControlValueDescriptor {
#[must_use] pub fn validate(&self, value: &ControlValue) -> bool {
#[must_use]
pub fn validate(&self, value: &ControlValue) -> bool {
match self {
ControlValueDescriptor::Null => {
if let &ControlValue::Null = value {
@@ -342,8 +357,11 @@ pub enum ControlValue {
}
impl ControlValue {
#[must_use] pub fn is_primitive(&self) -> bool {
matches!(self, ControlValue::Null
#[must_use]
pub fn is_primitive(&self) -> bool {
matches!(
self,
ControlValue::Null
| ControlValue::Integer(_)
| ControlValue::BitMask(_)
| ControlValue::Float(_)
@@ -351,7 +369,8 @@ impl ControlValue {
| ControlValue::Boolean(_)
| ControlValue::Binary(_)
| ControlValue::Area { .. }
| ControlValue::Orientation(_))
| ControlValue::Orientation(_)
)
}
// pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool {
@@ -370,7 +389,8 @@ impl ControlValue {
// false
// }
#[must_use] pub fn same_type(&self, other: &ControlValue) -> bool {
#[must_use]
pub fn same_type(&self, other: &ControlValue) -> bool {
match self {
ControlValue::Null => {
if let ControlValue::Null = other {
+24 -7
View File
@@ -1,13 +1,14 @@
use crate::error::NokhwaError;
use crate::frame_buffer::FrameBuffer;
use crate::types::CameraFormat;
use std::fmt::Debug;
pub use image::{ImageBuffer, Pixel, Primitive};
use crate::image::{DecodedImage, NonFloatScalarWidth};
use crate::types::{CameraFormat, Resolution};
pub use image::{ImageBuffer, Pixel, Primitive};
use std::fmt::Debug;
pub trait Decoder {
type Config: Clone + Debug + TryFrom<CameraFormat>;
type OutputMeta: Debug;
type Config: Clone + Debug;
type OutputMeta: Clone + Debug;
type DestinationFormatHint: Clone + Debug;
fn config(&self) -> &Self::Config;
@@ -17,6 +18,7 @@ pub trait Decoder {
&mut self,
to_decode: FrameBuffer,
buffer: impl AsMut<[u8]>,
destination_format_hint: Option<Self::DestinationFormatHint>,
) -> Result<Self::OutputMeta, NokhwaError>;
fn decode_to_pixel_buffer<P: Pixel>(
@@ -24,11 +26,26 @@ pub trait Decoder {
to_decode: FrameBuffer,
buffer: impl AsMut<[P::Subpixel]>,
) -> Result<Self::OutputMeta, NokhwaError>
where <P as Pixel>::Subpixel: NonFloatScalarWidth;
where
<P as Pixel>::Subpixel: NonFloatScalarWidth;
fn decode<P: Pixel>(
&mut self,
to_decode: FrameBuffer,
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
where <P as Pixel>::Subpixel: NonFloatScalarWidth;
where
<P as Pixel>::Subpixel: NonFloatScalarWidth;
fn output_decoder_min_size_pixel<P>(&self, resolution: Resolution) -> usize where
P: Pixel,
<P as Pixel>::Subpixel: NonFloatScalarWidth {
let channels = P::CHANNEL_COUNT as usize;
let width_bytes = <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES;
let resolution_mult = resolution.height() * resolution.width();
(resolution_mult as usize) * (width_bytes as usize) * channels
}
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize;
fn buffer_takes_destination_hint(&self) -> bool;
}
+16
View File
@@ -64,6 +64,22 @@ pub enum NokhwaError {
PermissionDenied,
#[error("Failed to decode: {0}")]
Decoder(String),
#[error("Unsupported FrameFormat: {0}")]
DecoderUnsupportedFrameFormat(FrameFormat),
#[error("Unsupported pixel configuration {0} with width {1}b.")]
DecoderUnsupportedDestinationPixelFormat(&'static str, u32),
#[error("Bad decoder configuration: {0}")]
DecoderInvalidConfiguration(String),
#[error("Failed to initialize decoder: {0}")]
DecoderInitializationError(String),
#[error("Bad frame sent to the decoder: {0}")]
DecoderInvalidFrameData(String),
#[error("Bad buffer sent to decoder, did not write: {0}")]
DecoderInvalidBuffer(String),
#[error("You need to pass in a destination hint, it is not optional for this decoder.")]
DecoderDestinationHintRequired,
#[error("Decoder already deinitialized. Unusable, please make a new decoder.")]
DecoderAlreadyDeinitialized
}
//
// pub enum InitializeError {}
+18 -15
View File
@@ -39,7 +39,8 @@ pub struct FormatRequest {
}
impl FormatRequest {
#[must_use] pub fn new(
#[must_use]
pub fn new(
format_request_type: FormatRequestType,
allowed_frame_formats: Vec<FrameFormat>,
) -> Self {
@@ -49,11 +50,13 @@ impl FormatRequest {
}
}
#[must_use] pub fn best<'a>(&self, camera_formats: &'a [CameraFormat]) -> Option<&'a CameraFormat> {
#[must_use]
pub fn best<'a>(&self, camera_formats: &'a [CameraFormat]) -> Option<&'a CameraFormat> {
camera_formats.first()
}
#[must_use] pub fn sort_foramts(&self, mut camera_formats: Vec<CameraFormat>) -> Vec<CameraFormat> {
#[must_use]
pub fn sort_foramts(&self, mut camera_formats: Vec<CameraFormat>) -> Vec<CameraFormat> {
if camera_formats.is_empty() {
return camera_formats;
}
@@ -77,35 +80,35 @@ impl FormatRequest {
camera_formats
.into_iter()
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
.filter(|fmt| self.allowed_frame_formats.contains(&fmt.format()))
.filter(|cam_fmt| {
if let Some(res_range) = resolution {
return res_range.validate(cam_fmt.resolution());
return res_range.validate(&cam_fmt.resolution());
}
if let Some(frame_rate_range) = frame_rate {
return frame_rate_range.validate(cam_fmt.frame_rate());
return frame_rate_range.validate(&cam_fmt.frame_rate());
}
true
})
.collect()
}
FormatRequestType::HighestFrameRate { frame_rate } => {
camera_formats.sort_by(|a, b| a.frame_rate().cmp(b.frame_rate()));
camera_formats.sort_by_key(CameraFormat::frame_rate);
camera_formats
.into_iter()
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
.filter(|a| frame_rate.validate(a.frame_rate()))
.filter(|fmt| self.allowed_frame_formats.contains(&fmt.format()))
.filter(|a| frame_rate.validate(&a.frame_rate()))
.collect()
}
FormatRequestType::HighestResolution { resolution } => {
camera_formats.sort_by(|a, b| a.resolution().cmp(b.resolution()));
camera_formats.sort_by_key(CameraFormat::frame_rate);
camera_formats
.into_iter()
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
.filter(|a| resolution.validate(a.resolution()))
.filter(|fmt| self.allowed_frame_formats.contains(&fmt.format()))
.filter(|a| resolution.validate(&a.resolution()))
.collect()
}
FormatRequestType::Exact {
@@ -113,8 +116,8 @@ impl FormatRequest {
frame_rate,
} => camera_formats
.into_iter()
.filter(|fmt| self.allowed_frame_formats.contains(fmt.format()))
.filter(|a| resolution.eq(a.resolution()) && frame_rate.eq(a.frame_rate()))
.filter(|fmt| self.allowed_frame_formats.contains(&fmt.format()))
.filter(|a| resolution.eq(&a.resolution()) && frame_rate.eq(&a.frame_rate()))
.collect(),
FormatRequestType::Any => {
// return as-is
@@ -132,7 +135,7 @@ pub fn format_distance_to_point(
format: &CameraFormat,
) -> f32 {
let frame_rate_distance = match frame_rate {
Some(f_point) => (format.frame_rate() - f_point)
Some(f_point) => (&format.frame_rate() - f_point)
.approximate_float()
.unwrap_or(f32::INFINITY)
.abs(),
+48 -11
View File
@@ -14,11 +14,11 @@
* limitations under the License.
*/
use crate::control::ControlValue;
pub use compact_str::CompactString;
pub use smallmap::Map;
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
pub use compact_str::CompactString;
pub use smallmap::Map;
pub type PlatformSpecificFlag = u32;
@@ -28,13 +28,15 @@ pub struct Metadata {
}
impl Metadata {
#[must_use] pub fn new() -> Self {
#[must_use]
pub fn new() -> Self {
Self {
flags: Map::default(),
}
}
#[must_use] pub fn get(&self, key: &str) -> Option<&ControlValue> {
#[must_use]
pub fn get(&self, key: &str) -> Option<&ControlValue> {
self.flags.get(key)
}
@@ -80,26 +82,48 @@ impl PartialEq for Metadata {
///
/// Note that decoding on the main thread **will** decrease your performance and lead to dropped frames.
#[derive(Clone, Debug, Hash, PartialEq)]
pub struct FrameBuffer {
buffer: Cow<'static, [u8]>,
pub struct FrameBuffer<'a> {
buffer: Cow<'a, [u8]>,
metadata: Option<Metadata>,
}
impl FrameBuffer {
impl<'a> FrameBuffer<'a> {
/// Creates a new buffer with a [`&[u8]`].
#[must_use]
#[inline]
pub fn new(buffer: Cow<'static, [u8]>, metadata: Option<Metadata>) -> Self {
pub fn new(buffer: Cow<'a, [u8]>, metadata: Option<Metadata>) -> Self {
Self { buffer, metadata }
}
/// Get the data of this buffer.
#[must_use]
pub fn buffer(&self) -> &[u8] {
pub fn buffer(&'a self) -> &'a [u8] {
&self.buffer
}
#[must_use] pub fn consume(self) -> (Cow<'static, [u8]>, Option<Metadata>) {
#[must_use]
pub fn len(&self) -> usize {
self.buffer.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
#[must_use]
pub fn deep_copy(&self) -> Self {
Self {
buffer: match &self.buffer {
Cow::Borrowed(b) => Cow::Owned(b.to_vec()),
Cow::Owned(o) => Cow::Owned(o.clone()),
},
metadata: self.metadata.clone(),
}
}
#[must_use]
pub fn consume(self) -> (Cow<'a, [u8]>, Option<Metadata>) {
(self.buffer, self.metadata)
}
@@ -107,5 +131,18 @@ impl FrameBuffer {
pub fn metadata(&self) -> Option<&Metadata> {
self.metadata.as_ref()
}
}
impl AsRef<[u8]> for FrameBuffer<'_> {
fn as_ref(&self) -> &[u8] {
self.buffer.as_ref()
}
}
impl Deref for FrameBuffer<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.buffer.as_ref()
}
}
+105 -238
View File
@@ -14,77 +14,24 @@
* limitations under the License.
*/
use std::fmt::{Display, Formatter};
use ordered_float::OrderedFloat;
// /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
// /// Note that endianness is determined by the native machine (or the driver itself).
// #[derive(Clone, Debug, Hash, PartialOrd, PartialEq)]
// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
// #[non_exhaustive]
// pub enum FrameFormat {
// // Compressed Formats
//
//
// // YCbCr Formats
//
// // 8 bit per pixel, 4:4:4
// Ayuv444,
//
// // -> 4:2:2
// Yuyv422, // AKA YUY2
// Uyvy422, // UYUV
// Yvyu422,
// Yv12,
//
// // 4:2:0
// Nv12,
// Nv21,
// I420,
//
// // 16:1:1
// Yvu9,
//
// // Grayscale Formats
// Luma8,
// Luma16,
//
// // Depth
// Depth16,
//
// // RGB Formats
// Rgb332,
// Rgb888,
// RgbA8888,
// ARgb8888,
// RgbX1010102,
// RgbA1010102,
// ARgb1010102,
//
//
// Bgr888,
// BgrA8888,
// Bgr121212,
// BgrA1212121212,
//
// Bgr161616,
// Bgr16161616,
//
//
// // Bayer Formats
// Bayer8,
// Bayer16,
//
// // Custom
// Custom(CustomFrameFormat),
// }
use std::fmt::{Display, Formatter};
macro_rules! define_frame_format_with_groups {
(
$(
$group_name:ident => [
$classifier:ident [
$(
$sub_classifier:ident [
$(
$group_name:expr => [
$($format:ident),* $(,)?
]
),* $(,)?
]
),* $(,)?
]
),* $(,)?
) => {
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
/// Note that endianness is determined by the native machine (or the driver itself), unless otherwise
@@ -98,247 +45,166 @@ macro_rules! define_frame_format_with_groups {
#[allow(non_camel_case_types)]
pub enum FrameFormat {
$(
$($format,)*
$(
$($($format,)*)*
)*
)*
Custom(CustomFrameFormat),
}
paste::paste! {
impl FrameFormat {
$(
pub const $group_name: &'static [FrameFormat] = &[
$(FrameFormat::$format),*
pub const $classifier: &'static [FrameFormat] = &[
$($($(FrameFormat::$format,)*)*)*
];
$(
pub const [<$classifier _ $sub_classifier>]: &'static [FrameFormat] = &[
$($(FrameFormat::$format,)*)*
];
$(
pub const [<$classifier _ $sub_classifier _ $group_name>]: &'static [FrameFormat] = &[
$(FrameFormat::$format,)*
];
)*
)*
)*
pub const ALL: &'static [FrameFormat] = &[
$(
$(FrameFormat::$format,)*
)*
$($(
$($(FrameFormat::$format,)*)*
)*)*
];
}
}
};
}
define_frame_format_with_groups! {
COMPRESSED => [
COMPRESSED [
MPEG [
H => [
H265,
HEVC,
H264,
AVC1,
H263,
AV1,
],
MPEG4 => [
AVC1,
MPEG_4,
XviD,
],
MPEG => [
MPEG_1,
MPEG_2,
MPEG_4,
],
],
IMAGE [
MJPEG => [
MJPEG,
XviD,
]
],
OPEN [
AOM => [
AV1
],
WEB => [
VP8,
VP9,
VP9
],
]
],
YCBCR_PACKED_444 => [
YCBCR [
PACKED [
444 => [
Ayuv_32,
],
YCBCR_PLANAR_444 => [],
YCBCR_SEMI_PLANAR_444 => [
NV24,
NV42,
],
YCBCR_PACKED_422 => [
422 => [
Yuyv_4_2_2,
Uyvy_4_2_2,
Vyuy_4_2_2,
Yvyu_4_2_2,
Y210,
Y216,
],
YCBCR_PLANAR_422 => [],
YCBCR_SEMI_PLANAR_422 => [
420 => [],
411 => [
]
],
PLANAR [
444 => [],
422 => [],
420 => [
Yuv_4_2_0,
],
411 => [
]
],
SEMI [
444 => [
NV24,
NV42,
],
422 => [
NV16,
NV61,
],
YCBCR_PACKED_420 => [],
YCBCR_PLANAR_420 => [
Yuv_4_2_0,
Yvu_4_2_0,
],
YCBCR_SEMI_PLANAR_420 => [
420 => [
NV12,
NV21,
P010,
P012,
],
YCBCR_PACKED_411 => [
Y41Packed
411 => [
]
],
YCBCR_PLANAR_411 => [
Y411Planar
],
YCBCR_SEMI_PLANAR_411 => [
NV11,
],
LUMA => [
BRIGHTNESS [
LUMA [
SMALL => [
Luma_8,
],
LARGE => [
Luma_10,
Luma_12,
Luma_14,
Luma_16,
Depth_16,
],
],
DEPTH [
SMALL => [],
LARGE => [Depth_16],
]
],
RAW_RGB => [
RAW [
RGB [
NO_ALPHA => [
Rgb_3_3_2,
Rgb_5_6_5,
Rgb_5_5_5,
Rgb_8_8_8,
],
WITH_ALPHA => [
Argb_8_8_8_8,
Rgba_8_8_8_8,
]
],
RAW_BGR => [
BGR [
NO_ALPHA => [
Bgr_3_3_2,
Bgr_5_6_5,
Bgr_5_5_5,
Bgr_8_8_8,
],
WITH_ALPHA => [
Abgr_8_8_8_8,
Bgra_8_8_8_8,
]
]
]
}
// define_frame_format_groups! {
// ALL => [
// // Compressed Formats
// H265,
// H264,
// Avc1,
// H263,
// Av1,
// Mpeg1,
// Mpeg2,
// Mpeg4,
// MJpeg,
// XVid,
// VP8,
// VP9,
//
// // YCbCr Formats
//
// // 8 bit per pixel, 4:4:4
// Ayuv444,
//
// // -> 4:2:2
// Yuyv422, // AKA YUY2
// Uyvy422, // UYUV
// Yvyu422,
// Yv12,
//
// // 4:2:0
// Nv12,
// Nv21,
// I420,
//
// // 16:1:1
// Yvu9,
//
// // Grayscale Formats
// Luma8,
// Luma16,
//
// // Depth
// Depth16,
//
// // RGB Formats
// Rgb332,
// Rgb888,
//
// Bgr888,
// BgrA8888,
//
// RgbA8888,
// ARgb8888,
//
// // Bayer Formats
// Bayer8,
// Bayer16,
// ],
// COMPRESSED => [
// H265,
// H264,
// Avc1,
// H263,
// Av1,
// Mpeg1,
// Mpeg2,
// Mpeg4,
// MJpeg,
// XVid,
// VP8,
// VP9,
// ],
// YCBCR => [
// Ayuv444,
//
// // -> 4:2:2
// Yuyv422, // AKA YUY2
// Uyvy422, // UYUV
// Yvyu422,
// Yv12,
//
// // 4:2:0
// Nv12,
// Nv21,
// I420,
//
// // 16:1:1
// Yvu9,
// ],
// YCBCR_PACKED => [
// Ayuv444,
//
// // -> 4:2:2
// Yuyv422, // AKA YUY2
// Uyvy422, // UYUV
// Yvyu422,
//
// // 4:2:0
// Nv12,
// Nv21,
// I420,
//
// ],
// YCBCR_PLANAR => [ Yvu9,
// Yv12,
// ],
// LUMA => [
// Luma8, Luma16
// ],
// RGB => [
// Rgb332, RgbA8888
// ],
// COLOR_FORMATS => [
// H265, H264, H263, Av1, Avc1, Mpeg1, Mpeg2, Mpeg4, MJpeg, XVid,
// VP8, VP9, Yuyv422, Uyvy422, Nv12, Nv21, Yv12, Rgb332, RgbA8888
// ],
// GRAYSCALE => [
// Luma8, Luma16, Depth16,
// ],
// RAW => [
// // RGB Formats
// Rgb332,
// Rgb888,
//
// Bgr888,
// BgrA8888,
//
// RgbA8888,
// ARgb8888,],
// BAYER => [
// // Bayer Formats
// Bayer8,
// Bayer16,],
// }
impl Display for FrameFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
@@ -346,6 +212,7 @@ impl Display for FrameFormat {
}
#[derive(Copy, Clone, Debug, Hash, PartialOrd, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum CustomFrameFormat {
UUID(u128),
FourCC([char; 4]),
+19 -20
View File
@@ -1,8 +1,8 @@
use bytemuck::Pod;
use image::{ImageBuffer, Pixel, Primitive};
use num_traits::{NumCast, PrimInt};
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use bytemuck::Pod;
use num_traits::{NumCast, PrimInt};
#[derive(Debug)]
pub struct DecodedImage<Px, Meta>
@@ -15,16 +15,14 @@ where
pub metadata: Meta,
}
impl<Px, Meta> DecodedImage<Px, Meta> where
impl<Px, Meta> DecodedImage<Px, Meta>
where
Px: Pixel,
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
Meta: Debug {
pub fn new(buffer: ImageBuffer<Px, Vec<Px::Subpixel>>,
metadata: Meta) -> Self {
Self {
buffer,
metadata,
}
Meta: Debug,
{
pub fn new(buffer: ImageBuffer<Px, Vec<Px::Subpixel>>, metadata: Meta) -> Self {
Self { buffer, metadata }
}
}
@@ -32,7 +30,7 @@ impl<Px, Meta> Deref for DecodedImage<Px, Meta>
where
Px: Pixel,
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
Meta: Debug
Meta: Debug,
{
type Target = ImageBuffer<Px, Vec<Px::Subpixel>>;
@@ -44,7 +42,8 @@ where
impl<Px, Meta> DerefMut for DecodedImage<Px, Meta>
where
Px: Pixel,
<Px as Pixel>::Subpixel: NonFloatScalarWidth,Meta: Debug
<Px as Pixel>::Subpixel: NonFloatScalarWidth,
Meta: Debug,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
@@ -55,24 +54,24 @@ where
// TODO: add more custom integer sizes, or break our dependence on image entirely and
// create our own imagebuffer
pub trait NonFloatScalarWidth: Debug + Primitive + PrimInt + NumCast + Pod {
const WIDTH: u32;
const WIDTH_BYTES: u32;
}
macro_rules! impl_nfsw {
( $( [ ( $( $things:ty ),+ ) : $size:literal ] ),* $(,)? ) => {
( $( [ ( $( $things:ty ),* ) : $size:literal ] ),* $(,)? ) => {
$(
$(
impl NonFloatScalarWidth for $things {
const WIDTH: u32 = $size;
const WIDTH_BYTES: u32 = $size;
}
)+
)*
)*
}
}
impl_nfsw! {
[ (u8, i8) : 8 ],
[ (u16, i16) : 16 ],
[ (u32, i32) : 32 ],
[ (u64, i64) : 64 ],
[ (u8, i8) : 1 ],
[ (u16, i16) : 2 ],
[ (u32, i32) : 4 ],
[ (u64, i64) : 8 ],
}
+1
View File
@@ -35,3 +35,4 @@ pub mod stream;
pub mod traits;
pub mod types;
pub mod utils;
mod metadata;
+12 -3
View File
@@ -31,12 +31,16 @@ pub trait PlatformTrait {
fn open(&mut self, index: CameraIndex) -> NokhwaResult<Self::Camera>;
fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult<Box<dyn Camera>> where <Self as PlatformTrait>::Camera: 'static {
fn open_dynamic(&mut self, index: CameraIndex) -> NokhwaResult<Box<dyn Camera>>
where
<Self as PlatformTrait>::Camera: 'static,
{
self.open(index).map(|cam| Box::new(cam) as Box<dyn Camera>)
}
}
#[cfg(feature = "async")]
#[cfg_attr(feature = "async", async_trait::async_trait)]
pub trait AsyncPlatformTrait: PlatformTrait {
const PLATFORM: Backends;
type AsyncCamera: crate::camera::AsyncCamera;
@@ -47,7 +51,12 @@ pub trait AsyncPlatformTrait: PlatformTrait {
async fn open_async(&mut self, index: &CameraIndex) -> NokhwaResult<Self::AsyncCamera>;
async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>> where <Self as AsyncPlatformTrait>::AsyncCamera: 'static {
self.open_async(index).await.map(|cam| Box::new(cam) as Box<dyn Camera>)
async fn open_dynamic_async(&mut self, index: &CameraIndex) -> NokhwaResult<Box<dyn Camera>>
where
<Self as AsyncPlatformTrait>::AsyncCamera: 'static,
{
self.open_async(index)
.await
.map(|cam| Box::new(cam) as Box<dyn Camera>)
}
}
+1 -6
View File
@@ -148,16 +148,11 @@ where
fn bool_to_inclusive_char(inclusive: bool, upper: bool) -> char {
if inclusive {
if upper {
']'
} else {
'['
}
if upper { ']' } else { '[' }
} else if upper {
')'
} else {
'('
}
}
+26 -20
View File
@@ -59,9 +59,9 @@ pub struct StreamConfiguration {
/// Possible events to receive from an active stream.
#[derive(Clone, Debug, PartialEq)]
pub enum Event {
pub enum Event<'a> {
/// A new frame.
NewFrame(FrameBuffer),
NewFrame(FrameBuffer<'a>),
/// Camera Format Changed.
///
/// This will usually require the reset of a buffer, or be followed by a [`Event::Terminated`],
@@ -97,17 +97,18 @@ pub enum Event {
///
/// You may also close the stream from the handle side using
#[derive(Debug)]
pub struct StreamHandle {
frame: Receiver<Event>,
pub struct StreamHandle<'a> {
frame: Receiver<Event<'a>>,
control: Arc<Sender<()>>,
configuration: StreamConfiguration,
format: Cell<CameraFormat>,
}
impl StreamHandle {
impl<'a> StreamHandle<'a> {
/// You shouldn't be here.
#[must_use] pub fn new(
recv: Receiver<Event>,
#[must_use]
pub fn new(
recv: Receiver<Event<'a>>,
control: Arc<Sender<()>>,
configuration: StreamConfiguration,
format: CameraFormat,
@@ -128,16 +129,16 @@ impl StreamHandle {
self.format.get()
}
pub fn next_event(&self) -> Result<Event, NokhwaError> {
pub fn next_event(&self) -> Result<Event<'_>, NokhwaError> {
let event = match self.configuration.receiver {
StreamReceiverBehaviour::Blocking => {
self.frame.recv().unwrap_or_else(|_| Event::Closed)
}
StreamReceiverBehaviour::Timeout(time) => self
.frame
.recv_timeout(time).unwrap_or_else(|_| Event::NotReady),
StreamReceiverBehaviour::Try => self.frame.try_recv().unwrap_or_else(
|why| match why {
.recv_timeout(time)
.unwrap_or_else(|_| Event::NotReady),
StreamReceiverBehaviour::Try => self.frame.try_recv().unwrap_or_else(|why| match why {
TryRecvError::Empty => Event::NotReady,
TryRecvError::Disconnected => Event::Closed,
}),
@@ -150,7 +151,7 @@ impl StreamHandle {
Ok(event)
}
pub fn next_frame(&self) -> Result<FrameBuffer, NokhwaError> {
pub fn next_frame(&self) -> Result<FrameBuffer<'_>, NokhwaError> {
loop {
let event = self.next_event()?;
match event {
@@ -159,7 +160,11 @@ impl StreamHandle {
let _ = self.control.try_send(());
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()));
}
Event::Other(why) => if self.configuration.on_other == ControlFlowOnOther::Break { return Err(NokhwaError::ReadFrameError(why)) },
Event::Other(why) => {
if self.configuration.on_other == ControlFlowOnOther::Break {
return Err(NokhwaError::ReadFrameError(why));
}
}
Event::Error(e) => return Err(NokhwaError::ReadFrameError(e.to_string())),
_ => {}
}
@@ -167,7 +172,7 @@ impl StreamHandle {
}
#[cfg(feature = "async")]
pub async fn poll_event(&self) -> Result<Event, NokhwaError> {
pub async fn poll_event(&self) -> Result<Event<'_>, NokhwaError> {
Ok(self.frame.recv_async().await.map_or_else(
|_| Event::Closed,
|e| {
@@ -181,7 +186,7 @@ impl StreamHandle {
// TODO: a smarter implementation? maybe?
#[cfg(feature = "async")]
pub async fn poll_next_frame(&self) -> Result<FrameBuffer, NokhwaError> {
pub async fn poll_next_frame(&self) -> Result<FrameBuffer<'_>, NokhwaError> {
loop {
let event = self.poll_event().await?;
match event {
@@ -190,17 +195,18 @@ impl StreamHandle {
let _ = self.control.try_send(());
return Err(NokhwaError::ReadFrameError("Stream Closed.".to_string()));
}
Event::Other(why) => match self.configuration.on_other {
ControlFlowOnOther::Continue => continue,
ControlFlowOnOther::Break => return Err(NokhwaError::ReadFrameError(why)),
},
Event::Other(why) => {
if let ControlFlowOnOther::Break = self.configuration.on_other {
return Err(NokhwaError::ReadFrameError(why));
}
}
_ => {}
}
}
}
}
impl Drop for StreamHandle {
impl Drop for StreamHandle<'_> {
fn drop(&mut self) {
let _ = self.control.try_send(());
}
+30 -24
View File
@@ -99,18 +99,18 @@ impl TryFrom<CameraIndex> for usize {
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct Resolution {
width_x: u32,
height_y: u32,
width: u32,
height: u32,
}
impl Resolution {
/// Create a new resolution from 2 image size coordinates.
#[must_use]
// TODO: make this height and width.
pub const fn new(x: u32, y: u32) -> Self {
pub const fn new(width: u32, height: u32) -> Self {
Resolution {
width_x: x,
height_y: y,
width,
height,
}
}
@@ -118,33 +118,33 @@ impl Resolution {
#[must_use]
#[inline]
pub fn width(self) -> u32 {
self.width_x
self.width
}
/// Get the height of Resolution
#[must_use]
#[inline]
pub fn height(self) -> u32 {
self.height_y
self.height
}
/// Get the x (width) of Resolution
#[must_use]
#[inline]
pub fn x(self) -> u32 {
self.width_x
self.width
}
/// Get the y (height) of Resolution
#[must_use]
#[inline]
pub fn y(self) -> u32 {
self.height_y
self.height
}
#[must_use]
pub fn aspect_ratio(&self) -> f64 {
f64::from(self.width_x) / f64::from(self.height_y)
f64::from(self.width) / f64::from(self.height)
}
}
@@ -232,31 +232,37 @@ pub struct FrameRate {
}
impl FrameRate {
#[must_use] pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
#[must_use]
pub const fn new(numerator: i32, denominator: NonZeroI32) -> Self {
Self {
rational: Rational32::new_raw(numerator, denominator.get()),
}
}
#[must_use] pub const fn from_fps(fps: i32) -> Self {
#[must_use]
pub const fn from_fps(fps: i32) -> Self {
Self {
rational: Rational32::new_raw(fps, 1),
}
}
#[must_use] pub fn numerator(&self) -> i32 {
#[must_use]
pub fn numerator(&self) -> i32 {
*self.rational.numer()
}
#[must_use] pub fn denominator(&self) -> i32 {
#[must_use]
pub fn denominator(&self) -> i32 {
*self.rational.denom()
}
#[must_use] pub fn as_raw(&self) -> &Rational32 {
#[must_use]
pub fn as_raw(&self) -> &Rational32 {
&self.rational
}
#[must_use] pub fn approximate_float(&self) -> Option<f32> {
#[must_use]
pub fn approximate_float(&self) -> Option<f32> {
let numerator_float = f32::from_i32(self.numerator())?;
let denominator_float = f32::from_i32(self.denominator())?;
@@ -346,8 +352,8 @@ impl CameraFormat {
pub const fn new_from(res_x: u32, res_y: u32, format: FrameFormat, fps: FrameRate) -> Self {
CameraFormat {
resolution: Resolution {
width_x: res_x,
height_y: res_y,
width: res_x,
height: res_y,
},
format,
frame_rate: fps,
@@ -356,8 +362,8 @@ impl CameraFormat {
/// Get the resolution of the current [`CameraFormat`]
#[must_use]
pub fn resolution(&self) -> &Resolution {
&self.resolution
pub fn resolution(&self) -> Resolution {
self.resolution
}
/// Get the width of the resolution of the current [`CameraFormat`]
@@ -379,8 +385,8 @@ impl CameraFormat {
/// Get the frame rate of the current [`CameraFormat`]
#[must_use]
pub fn frame_rate(&self) -> &FrameRate {
&self.frame_rate
pub fn frame_rate(&self) -> FrameRate {
self.frame_rate
}
/// Set the [`CameraFormat`]'s frame rate.
@@ -390,8 +396,8 @@ impl CameraFormat {
/// Get the [`CameraFormat`]'s format.
#[must_use]
pub fn format(&self) -> &FrameFormat {
&self.format
pub fn format(&self) -> FrameFormat {
self.format
}
/// Set the [`CameraFormat`]'s format.
+7 -3
View File
@@ -7,9 +7,9 @@ edition = "2024"
ffmpeg = ["ffmpeg-the-third"]
av1 = ["rav1e"]
yuyv = ["dcv-color-primitives", "yuv"]
mjpeg = ["zune-jpeg"]
wasm = []
mega = ["ffmpeg", "av1", "yuyv", "mjpeg"]
mjpeg = ["zune-jpeg", "zune-core"]
browser = []
general = ["ffmpeg", "av1", "yuyv", "mjpeg"]
static = ["ffmpeg-the-third/static"]
async = []
@@ -37,5 +37,9 @@ version = "0.7"
optional = true
[dependencies.zune-jpeg]
version = "0.5.0-rc7"
optional = true
[dependencies.zune-core]
version = "0.5.0-rc2"
optional = true
+315 -170
View File
@@ -1,10 +1,17 @@
use core::mem::transmute;
use bytemuck::try_cast_slice_mut;
use core::mem::transmute;
use ffmpeg_the_third::codec::{Context, Id, Parameters};
use ffmpeg_the_third::decoder::Video;
use ffmpeg_the_third::ffi::{av_frame_alloc, av_frame_move_ref, av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context, avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext, sws_scale_frame, AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace, AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket, AVPacketSideData, AVPixelFormat, AVRational, SwsContext};
use ffmpeg_the_third::{decoder, packet::Packet, Frame};
use ffmpeg_the_third::ffi::{
AVChromaLocation, AVCodecID, AVCodecParameters, AVColorPrimaries, AVColorRange, AVColorSpace,
AVColorTransferCharacteristic, AVFieldOrder, AVMediaType, AVPacket,
AVPixelFormat, AVRational, SwsContext, av_frame_alloc, av_frame_move_ref,
av_image_copy_to_buffer, av_image_fill_arrays, av_image_get_buffer_size, avcodec_free_context,
avcodec_parameters_alloc, avcodec_parameters_free, sws_freeContext, sws_getContext,
sws_scale_frame,
};
use ffmpeg_the_third::packet::{Borrow, Ref};
use ffmpeg_the_third::{Frame, decoder, packet::Packet};
use nokhwa_core::codec::Codec;
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive};
use nokhwa_core::error::NokhwaError;
@@ -19,40 +26,38 @@ pub struct FfmpegDecoder {
}
impl FfmpegDecoder {
pub fn new(config: <FfmpegDecoder as nokhwa_core::decoder::Decoder>::Config) -> Result<Self, NokhwaError> {
pub fn new(
config: <FfmpegDecoder as nokhwa_core::decoder::Decoder>::Config,
) -> Result<Self, NokhwaError> {
let codec = FfmpegCodec::new(config)?;
Ok(
Self {
codec,
sws: None,
}
)
Ok(Self { codec, sws: None })
}
pub fn with_format(format: &CameraFormat) -> Result<Self, NokhwaError> {
let config = FfmpegDecoderConfig::with_camera_format(format);
let codec = FfmpegCodec::new(config)?;
Ok(
Self {
codec,
sws: None,
}
)
Ok(Self { codec, sws: None })
}
pub fn receive_decoded_frame(&mut self, to_decode: FrameBuffer) -> Result<(Frame, <FfmpegDecoder as Decoder>::OutputMeta) , NokhwaError> {
pub fn receive_decoded_frame(
&mut self,
to_decode: FrameBuffer,
) -> Result<(Frame, <FfmpegDecoder as Decoder>::OutputMeta), NokhwaError> {
let new_pkt = Packet::borrow(to_decode.buffer());
self.codec.send_item(new_pkt.into())?;
let mut new_frame = unsafe { Frame::empty() };
let metadata = self.codec.receive_decoded_item(&mut new_frame).map_err(|why| NokhwaError::Decoder(why.to_string()))?;
let metadata = self
.codec
.receive_decoded_item(&mut new_frame)
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
Ok((new_frame, metadata))
}
}
impl Decoder for FfmpegDecoder {
type Config = <FfmpegCodec as Codec>::Config;
type OutputMeta = <FfmpegCodec as Codec>::WrittenMeta;
type DestinationFormatHint = AVPixelFormat;
fn config(&self) -> &Self::Config {
self.codec.config()
@@ -63,37 +68,77 @@ impl Decoder for FfmpegDecoder {
Ok(())
}
fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>) -> Result<Self::OutputMeta, NokhwaError> {
fn decode_to_buffer(
&mut self,
to_decode: FrameBuffer,
mut buffer: impl AsMut<[u8]>,
_destination_format: Option<Self::DestinationFormatHint>
) -> Result<Self::OutputMeta, NokhwaError> {
// TODO: add an extra zippy happy path for rgb/bgr/luma
let (frame, metadata) = self.receive_decoded_frame(to_decode)?;
let av_frame_data = unsafe { frame.as_ref().ok_or(NokhwaError::Decoder("No Frame Data from Decoder".to_string()))? };
let av_frame_data = unsafe {
frame.as_ref().ok_or(NokhwaError::Decoder(
"No Frame Data from Decoder".to_string(),
))?
};
let buffer = buffer.as_mut();
let result = unsafe {
av_image_copy_to_buffer(buffer.as_mut().as_mut_ptr(), buffer.as_mut().len() as i32, av_frame_data.data.as_ptr() as *const *const u8, av_frame_data.linesize.as_ptr(), metadata.pixel_format, self.config().resolution.width() as i32, self.config().resolution.height() as i32, 1)
av_image_copy_to_buffer(
buffer.as_mut().as_mut_ptr(),
buffer.as_mut().len() as i32,
av_frame_data.data.as_ptr() as *const *const u8,
av_frame_data.linesize.as_ptr(),
metadata.pixel_format,
self.config().resolution.width() as i32,
self.config().resolution.height() as i32,
1,
)
};
if result.is_negative() {
return Err(NokhwaError::Decoder(format!("Error Code {}", result)))
return Err(NokhwaError::Decoder(format!("Error Code {result}")));
}
Ok(metadata)
}
fn decode_to_pixel_buffer<P: Pixel>(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError> where <P as Pixel>::Subpixel: NonFloatScalarWidth {
let destination_format = pixel_to_destination_px_fmt::<P>(<P::Subpixel as NonFloatScalarWidth>::WIDTH).ok_or(NokhwaError::Decoder("Unsupported Pixel Type".to_string()))?;
fn decode_to_pixel_buffer<P: Pixel>(
&mut self,
to_decode: FrameBuffer,
mut buffer: impl AsMut<[P::Subpixel]>,
) -> Result<Self::OutputMeta, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
let destination_format =
pixel_to_destination_px_fmt::<P>()
.ok_or(NokhwaError::DecoderInvalidBuffer("Unsupported Pixel Type".to_string()))?;
let buffer = buffer.as_mut();
let estimated_size = self.codec.preferred_buffer_min_size(&None)?.ok_or(NokhwaError::Decoder("failed to estimate decoder buffer.length".to_string()))?;
let estimated_size =
self.codec
.preferred_buffer_min_size(&None)?
.ok_or(NokhwaError::DecoderInvalidBuffer(
"failed to estimate decoder buffer.length".to_string(),
))?;
if buffer.len() < estimated_size {
return Err(NokhwaError::Decoder("buffer too small!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer("buffer too small!".to_string()));
}
let (mut frame, decoded_meta) = self.receive_decoded_frame(to_decode)?;
let source_format = decoded_meta.pixel_format;
let cast_slice = try_cast_slice_mut::<P::Subpixel, u8>(buffer).map_err(|why| NokhwaError::Decoder(why.to_string()))?;
let cast_slice = try_cast_slice_mut::<P::Subpixel, u8>(buffer)
.map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?;
let receiving_buffer = unsafe {
let av_frame = av_frame_alloc();
let result = av_image_fill_arrays((*av_frame).data.as_mut_ptr(), (*av_frame).linesize.as_mut_ptr(), cast_slice.as_ptr(), destination_format, self.codec.config.resolution.width() as i32, self.codec.config.resolution.height() as i32, 1);
let result = av_image_fill_arrays(
(*av_frame).data.as_mut_ptr(),
(*av_frame).linesize.as_mut_ptr(),
cast_slice.as_ptr(),
destination_format,
self.codec.config.resolution.width() as i32,
self.codec.config.resolution.height() as i32,
1,
);
if result < 0 {
return Err(NokhwaError::Decoder("Failed to fill avimage".to_string()));
}
@@ -103,7 +148,11 @@ impl Decoder for FfmpegDecoder {
if source_format != destination_format {
let sws_scaler = match &mut self.sws {
None => {
let context = create_sws_context(source_format, destination_format, self.codec.config.resolution)?;
let context = create_sws_context(
source_format,
destination_format,
self.codec.config.resolution,
)?;
self.sws = Some(Sws {
sws: context,
source_pixel_format: source_format,
@@ -112,78 +161,134 @@ impl Decoder for FfmpegDecoder {
self.sws.as_mut().unwrap()
}
Some(v) => {
if v.source_pixel_format != source_format || v.dest_pixel_format != destination_format {
if v.source_pixel_format != source_format
|| v.dest_pixel_format != destination_format
{
v.source_pixel_format = source_format;
v.dest_pixel_format = destination_format;
v.sws = create_sws_context(source_format, destination_format, self.codec.config.resolution)?;
v.sws = create_sws_context(
source_format,
destination_format,
self.codec.config.resolution,
)?;
}
v
}
};
let scaled = unsafe {sws_scale_frame(sws_scaler.sws, receiving_buffer, frame.as_mut_ptr())};
let scaled =
unsafe { sws_scale_frame(sws_scaler.sws, receiving_buffer, frame.as_mut_ptr()) };
if scaled < 0 {
Err(NokhwaError::Decoder("Failed to scale sws".to_string()))
} else {
Ok(decoded_meta)
}
} else {
unsafe {
av_frame_move_ref(receiving_buffer, frame.as_mut_ptr())
};
unsafe { av_frame_move_ref(receiving_buffer, frame.as_mut_ptr()) };
Ok(decoded_meta)
}
}
fn decode<P: Pixel>(&mut self, to_decode: FrameBuffer) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError> where <P as Pixel>::Subpixel: NonFloatScalarWidth {
let min_size = P::CHANNEL_COUNT as usize * (<P::Subpixel as NonFloatScalarWidth>::WIDTH as usize / 8_usize) * self.codec.config.resolution.height() as usize * self.codec.config.resolution.width() as usize;
fn decode<P: Pixel>(
&mut self,
to_decode: FrameBuffer,
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
let min_size = self.output_decoder_min_size_pixel::<P>(self.config().resolution);
let mut buffer: Vec<P::Subpixel> = vec![<P::Subpixel>::DEFAULT_MIN_VALUE; min_size];
let meta = self.decode_to_buffer(to_decode, try_cast_slice_mut(&mut buffer).map_err(|why| NokhwaError::Decoder(why.to_string()))?)?;
Ok(DecodedImage::new(ImageBuffer::from_vec(self.codec.config.resolution.width(), self.codec.config.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to create Image Buffer".to_string()))?, meta))
}
let meta = self.decode_to_buffer(to_decode, try_cast_slice_mut(&mut buffer).map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?, None)?;
Ok(DecodedImage::new(
ImageBuffer::from_vec(
self.codec.config.resolution.width(),
self.codec.config.resolution.height(),
buffer,
)
.ok_or(NokhwaError::Decoder(
"Failed to create Image Buffer".to_string(),
))?,
meta,
))
}
fn create_sws_context(src_format: AVPixelFormat, dest: AVPixelFormat, resolution: Resolution) -> Result<*mut SwsContext, NokhwaError> {
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
let size =
unsafe { av_image_get_buffer_size(destination_format, resolution.width() as i32, resolution.height() as i32, 1) };
size as usize
}
fn buffer_takes_destination_hint(&self) -> bool {
false
}
}
fn create_sws_context(
src_format: AVPixelFormat,
dest: AVPixelFormat,
resolution: Resolution,
) -> Result<*mut SwsContext, NokhwaError> {
let param = 0_f64;
let new_sws = unsafe {
sws_getContext(resolution.width() as i32, resolution.height() as i32, src_format, resolution.width() as i32, resolution.height() as i32, dest, 0, core::ptr::null_mut(), core::ptr::null_mut(), &param)
sws_getContext(
resolution.width() as i32,
resolution.height() as i32,
src_format,
resolution.width() as i32,
resolution.height() as i32,
dest,
0,
core::ptr::null_mut(),
core::ptr::null_mut(),
&param,
)
};
Ok(new_sws)
}
fn pixel_to_destination_px_fmt<P: Pixel>(width: u32) -> Option<AVPixelFormat> where <P as Pixel>::Subpixel: NonFloatScalarWidth {
fn pixel_to_destination_px_fmt<P: Pixel>() -> Option<AVPixelFormat>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
match P::COLOR_MODEL {
"RGB" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_RGB24),
"RGB" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_RGB24),
_ => None,
}
},
"RGBA" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_RGBA),
16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_RGBA64LE, AVPixelFormat::AV_PIX_FMT_RGBA64BE)),
"RGBA" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_RGBA),
2 => Some(switch_endian(
AVPixelFormat::AV_PIX_FMT_RGBA64LE,
AVPixelFormat::AV_PIX_FMT_RGBA64BE,
)),
_ => None,
}
"BGR" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_BGR24),
},
"BGR" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_BGR24),
_ => None,
}
},
"BGRA" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_BGRA),
16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_BGRA64LE, AVPixelFormat::AV_PIX_FMT_BGRA64BE)),
"BGRA" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_BGRA),
2 => Some(switch_endian(
AVPixelFormat::AV_PIX_FMT_BGRA64LE,
AVPixelFormat::AV_PIX_FMT_BGRA64BE,
)),
_ => None,
}
"Y" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8),
16 => Some(switch_endian(AVPixelFormat::AV_PIX_FMT_GRAY16LE, AVPixelFormat::AV_PIX_FMT_GRAY16BE)),
},
"Y" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8),
2 => Some(switch_endian(
AVPixelFormat::AV_PIX_FMT_GRAY16LE,
AVPixelFormat::AV_PIX_FMT_GRAY16BE,
)),
_ => None,
}
"YA" => match width {
8 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8A),
},
"YA" => match <<P as Pixel>::Subpixel>::WIDTH_BYTES {
1 => Some(AVPixelFormat::AV_PIX_FMT_GRAY8A),
_ => None,
}
},
_ => None,
}
}
@@ -206,7 +311,6 @@ impl Drop for Sws {
}
}
#[derive(Clone, Debug)]
pub struct FfmpegFrameMetadata {
pub color_range: AVColorRange,
@@ -216,7 +320,7 @@ pub struct FfmpegFrameMetadata {
pub enum PacketOrRef<'a> {
Packet(Packet),
Ref(Borrow<'a>)
Ref(Borrow<'a>),
}
impl Ref for PacketOrRef<'_> {
@@ -249,20 +353,33 @@ pub struct FfmpegCodec {
impl FfmpegCodec {
fn new(config: <FfmpegCodec as Codec>::Config) -> Result<Self, NokhwaError> {
let id = convert_format_to_codec_id(&config.frame_format).ok_or(NokhwaError::Decoder("Could not find Format".to_string()))?;
let id = convert_format_to_codec_id(&config.frame_format)
.ok_or(NokhwaError::DecoderUnsupportedFrameFormat(config.frame_format))?;
let codec = decoder::find(id).ok_or(NokhwaError::Decoder("Failed to find codec".to_string()))?;
let codec =
decoder::find(id).ok_or(NokhwaError::DecoderInitializationError("Failed to find codec".to_string()))?;
let context = unsafe {
let ptr = ffmpeg_the_third::ffi::avcodec_alloc_context3(codec.as_ptr());
if ptr.is_null() {
return Err(NokhwaError::Decoder("ffmpeg returned a null context".to_string()))
return Err(NokhwaError::DecoderInitializationError(
"ffmpeg returned a null context".to_string(),
));
}
Context::wrap(ptr, None)
};
let mut video = context.decoder().video().map_err(|why| NokhwaError::Decoder(why.to_string()))?;
video.set_parameters(unsafe { Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::Decoder("Failed to convert parameters".to_string()))? }).map_err(|why| NokhwaError::Decoder(why.to_string()))?;
let mut video = context
.decoder()
.video()
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
video
.set_parameters(unsafe {
Parameters::from_raw(config.as_ptr()?).ok_or(NokhwaError::DecoderInitializationError(
"Failed to convert parameters".to_string(),
))?
})
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
Ok(Self {
decoder: video,
@@ -274,32 +391,36 @@ impl FfmpegCodec {
}
impl Codec for FfmpegCodec {
type Config = FfmpegDecoderConfig;
type Input<'a> = PacketOrRef<'a>;
type Output = Frame;
type Output<'a> = Frame;
type WrittenMeta = FfmpegFrameMetadata;
fn allowed_formats(&self) -> Result<&[FrameFormat], NokhwaError> {
if self.deinitialized {
return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string()))
return Err(NokhwaError::DecoderAlreadyDeinitialized);
}
Ok(FrameFormat::ALL)
}
fn config(&self) -> &Self::Config {
&self.config
}
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
if self.deinitialized {
return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string()))
return Err(NokhwaError::DecoderAlreadyDeinitialized);
}
dealloc_av_params(&mut self.temp_cfg);
let mut temp_config = config.as_avcodec_params()?;
self.decoder.set_parameters(unsafe { Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::Decoder("Failed to convert parameters".to_string()))? }).map_err(|why| NokhwaError::Decoder(why.to_string()))?;
self.decoder
.set_parameters(unsafe {
Parameters::from_raw(&mut temp_config).ok_or(NokhwaError::DecoderInvalidConfiguration(
"Failed to convert parameters".to_string(),
))?
})
.map_err(|why| NokhwaError::DecoderInvalidConfiguration(why.to_string()))?;
self.config = config;
self.temp_cfg = Some(&mut temp_config);
Ok(())
@@ -307,19 +428,30 @@ impl Codec for FfmpegCodec {
fn send_item(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError> {
if self.deinitialized {
return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string()))
return Err(NokhwaError::DecoderAlreadyDeinitialized);
}
self.decoder.send_packet(&input).map_err(|why| NokhwaError::Decoder(why.to_string()))
self.decoder
.send_packet(&input)
.map_err(|why| NokhwaError::Decoder(why.to_string()))
}
fn receive_decoded_item(&mut self, writing_output: &mut Self::Output) -> Result<FfmpegFrameMetadata, NokhwaError> {
fn receive_decoded_item(
&mut self,
writing_output: &mut Self::Output<'_>,
) -> Result<FfmpegFrameMetadata, NokhwaError> {
if self.deinitialized {
return Err(NokhwaError::Decoder("This decoder is deinitialized - it is no longer usable.".to_string()))
return Err(NokhwaError::DecoderAlreadyDeinitialized);
}
self.decoder.receive_frame(writing_output).map_err(|why| NokhwaError::Decoder(why.to_string()))?;
self.decoder
.receive_frame(writing_output)
.map_err(|why| NokhwaError::Decoder(why.to_string()))?;
let avframe = match unsafe { writing_output.as_ref() } {
Some(r) => r,
None => return Err(NokhwaError::Decoder("failed to get refrenece to decoded frame".to_string()))
None => {
return Err(NokhwaError::Decoder(
"failed to get refrenece to decoded frame".to_string(),
));
}
};
let meta = FfmpegFrameMetadata {
color_range: avframe.color_range,
@@ -329,26 +461,32 @@ impl Codec for FfmpegCodec {
Ok(meta)
}
fn preferred_buffer_min_size(&mut self, camera_format: &Option<CameraFormat>) -> Result<Option<usize>, NokhwaError> {
fn preferred_buffer_min_size(
&mut self,
camera_format: &Option<CameraFormat>,
) -> Result<Option<usize>, NokhwaError> {
let (width, height, pixel_format) = match camera_format {
Some(fmt) => {(
Some(fmt) => (
fmt.width(),
fmt.height(),
convert_frame_format_to_pixfmt(fmt.format()))
}
None => {
( self.config().resolution.width(), self.config.resolution.height(), convert_frame_format_to_pixfmt(&self.config().frame_format)
) }
convert_frame_format_to_pixfmt(&fmt.format()),
),
None => (
self.config().resolution.width(),
self.config.resolution.height(),
convert_frame_format_to_pixfmt(&self.config().frame_format),
),
};
let size = unsafe { av_image_get_buffer_size(pixel_format, width as i32, height as i32, 1) };
let size =
unsafe { av_image_get_buffer_size(pixel_format, width as i32, height as i32, 1) };
Ok(Some(size as usize))
}
fn deinitialize(&mut self) -> Result<(), NokhwaError> {
dealloc_av_params(&mut self.temp_cfg);
if self.deinitialized {
return Ok(())
return Ok(());
}
self.deinitialized = true;
@@ -396,9 +534,7 @@ fn convert_format_to_codec_id(frame_format: &FrameFormat) -> Option<Id> {
FrameFormat::Custom(c) => {
if let CustomFrameFormat::U32(c_id) = c {
// SAFETY: /shrug
let av_codec_id: AVCodecID = unsafe {
transmute(*c_id)
};
let av_codec_id: AVCodecID = unsafe { transmute(*c_id) };
Some(av_codec_id.into())
} else {
None
@@ -416,73 +552,86 @@ fn convert_frame_format_to_pixfmt(frame_format: &FrameFormat) -> AVPixelFormat {
FrameFormat::Yuyv_4_2_2 => AVPixelFormat::AV_PIX_FMT_YUYV422,
FrameFormat::Uyvy_4_2_2 => AVPixelFormat::AV_PIX_FMT_UYVY422,
FrameFormat::Yvyu_4_2_2 => AVPixelFormat::AV_PIX_FMT_YVYU422,
FrameFormat::Y210 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_Y210LE } else {
AVPixelFormat::AV_PIX_FMT_Y210BE }
},
FrameFormat::Y216 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_YUV422P16LE } else {
AVPixelFormat::AV_PIX_FMT_YUV422P16BE }
}
FrameFormat::NV16 => AVPixelFormat::AV_PIX_FMT_NV16,
FrameFormat::Yuv_4_2_0 => AVPixelFormat::AV_PIX_FMT_YUV420P,
FrameFormat::NV12 => AVPixelFormat::AV_PIX_FMT_NV12,
FrameFormat::NV21 => AVPixelFormat::AV_PIX_FMT_NV21,
FrameFormat::P010 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_P010LE } else {
AVPixelFormat::AV_PIX_FMT_P010BE }
},
AVPixelFormat::AV_PIX_FMT_P010LE
} else {
AVPixelFormat::AV_PIX_FMT_P010BE
}
}
FrameFormat::P012 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_P012LE } else {
AVPixelFormat::AV_PIX_FMT_P012BE }
},
FrameFormat::Y411Planar => AVPixelFormat::AV_PIX_FMT_YUV411P,
AVPixelFormat::AV_PIX_FMT_P012LE
} else {
AVPixelFormat::AV_PIX_FMT_P012BE
}
}
FrameFormat::Luma_8 => AVPixelFormat::AV_PIX_FMT_GRAY8,
FrameFormat::Luma_10 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_GRAY10LE } else {
AVPixelFormat::AV_PIX_FMT_GRAY10BE }
},
AVPixelFormat::AV_PIX_FMT_GRAY10LE
} else {
AVPixelFormat::AV_PIX_FMT_GRAY10BE
}
}
FrameFormat::Luma_12 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_GRAY12LE } else {
AVPixelFormat::AV_PIX_FMT_GRAY12BE }
},
AVPixelFormat::AV_PIX_FMT_GRAY12LE
} else {
AVPixelFormat::AV_PIX_FMT_GRAY12BE
}
}
FrameFormat::Luma_14 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_GRAY14LE } else {
AVPixelFormat::AV_PIX_FMT_GRAY14BE }
},
AVPixelFormat::AV_PIX_FMT_GRAY14LE
} else {
AVPixelFormat::AV_PIX_FMT_GRAY14BE
}
}
FrameFormat::Luma_16 | FrameFormat::Depth_16 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_GRAY16LE } else {
AVPixelFormat::AV_PIX_FMT_GRAY16BE }
},
AVPixelFormat::AV_PIX_FMT_GRAY16LE
} else {
AVPixelFormat::AV_PIX_FMT_GRAY16BE
}
}
FrameFormat::Rgb_3_3_2 => AVPixelFormat::AV_PIX_FMT_RGB8,
FrameFormat::Rgb_5_6_5 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_RGB565LE } else {
AVPixelFormat::AV_PIX_FMT_RGB565BE }
AVPixelFormat::AV_PIX_FMT_RGB565LE
} else {
AVPixelFormat::AV_PIX_FMT_RGB565BE
}
}
FrameFormat::Rgb_5_5_5 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_RGB555LE
} else {
AVPixelFormat::AV_PIX_FMT_RGB565BE
}
}
FrameFormat::Rgb_5_5_5 => if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_RGB555LE } else {
AVPixelFormat::AV_PIX_FMT_RGB565BE }
FrameFormat::Rgb_8_8_8 => AVPixelFormat::AV_PIX_FMT_RGB24,
FrameFormat::Argb_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_ARGB,
FrameFormat::Rgba_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_RGBA,
FrameFormat::Bgr_3_3_2 => AVPixelFormat::AV_PIX_FMT_BGR8,
FrameFormat::Bgr_5_6_5 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_BGR565LE } else {
AVPixelFormat::AV_PIX_FMT_BGR565BE }
AVPixelFormat::AV_PIX_FMT_BGR565LE
} else {
AVPixelFormat::AV_PIX_FMT_BGR565BE
}
}
FrameFormat::Bgr_5_5_5 => {
if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_BGR555LE
} else {
AVPixelFormat::AV_PIX_FMT_BGR555BE
}
}
FrameFormat::Bgr_5_5_5 => if is_little_endian() {
AVPixelFormat::AV_PIX_FMT_BGR555LE } else {
AVPixelFormat::AV_PIX_FMT_BGR555BE }
FrameFormat::Bgr_8_8_8 => AVPixelFormat::AV_PIX_FMT_BGR24,
FrameFormat::Abgr_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_ABGR,
FrameFormat::Bgra_8_8_8_8 => AVPixelFormat::AV_PIX_FMT_BGRA,
@@ -490,19 +639,10 @@ fn convert_frame_format_to_pixfmt(frame_format: &FrameFormat) -> AVPixelFormat {
}
}
#[derive(Clone, Debug)]
pub struct FfmpegDecoderConfig {
pub frame_format: FrameFormat,
pub codec_tag: u32,
#[doc = " Extra binary data needed for initializing the decoder, codec-dependent.\n\n Must be allocated with av_malloc() and will be freed by\n avcodec_parameters_free(). The allocated size of extradata must be at\n least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding\n bytes zeroed."]
pub extra_data: Option<*mut u8>,
#[doc = " Size of the extradata content in bytes."]
pub extra_data_size: usize,
#[doc = " Additional data associated with the entire stream.\n\n Should be allocated with av_packet_side_data_new() or\n av_packet_side_data_add(), and will be freed by avcodec_parameters_free()."]
pub coded_side_data: Option<*mut AVPacketSideData>,
#[doc = " Amount of entries in @ref coded_side_data."]
pub coded_side_data_size: usize,
#[doc = " - video: the pixel format, the value corresponds to enum AVPixelFormat.\n - audio: the sample format, the value corresponds to enum AVSampleFormat."]
pub pix_fmt: Option<AVPixelFormat>,
#[doc = " Codec-specific bitstream restrictions that the stream conforms to."]
@@ -533,20 +673,20 @@ impl FfmpegDecoderConfig {
pub fn as_avcodec_params(&self) -> Result<AVCodecParameters, NokhwaError> {
let mut av_codec_params = unsafe { avcodec_parameters_alloc().read() };
av_codec_params.codec_type = AVMediaType::AVMEDIA_TYPE_VIDEO;
if let Some(extra_data) = self.extra_data {
if extra_data.is_null() {
return Err(NokhwaError::Decoder("extra data is nullptr!".to_string()))
}
av_codec_params.extradata = extra_data;
av_codec_params.extradata_size = self.extra_data_size as i32;
}
if let Some(side_data) = self.coded_side_data {
if side_data.is_null() {
return Err(NokhwaError::Decoder("side data is nullptr!".to_string()))
}
av_codec_params.coded_side_data = side_data;
av_codec_params.nb_coded_side_data = self.coded_side_data_size as i32;
}
// if let Some(extra_data) = self.extra_data {
// if extra_data.is_null() {
// return Err(NokhwaError::Decoder("extra data is nullptr!".to_string()))
// }
// av_codec_params.extradata = extra_data;
// av_codec_params.extradata_size = self.extra_data_size as i32;
// }
// if let Some(side_data) = self.coded_side_data {
// if side_data.is_null() {
// return Err(NokhwaError::Decoder("side data is nullptr!".to_string()))
// }
// av_codec_params.coded_side_data = side_data;
// av_codec_params.nb_coded_side_data = self.coded_side_data_size as i32;
// }
if let Some(id) = convert_format_to_codec_id(&self.frame_format) {
av_codec_params.codec_id = id.into();
@@ -558,7 +698,9 @@ impl FfmpegDecoderConfig {
av_codec_params.format = pixel_format as i32;
}
} else {
return Err(NokhwaError::Decoder("Failed to convert frameformat to id".to_string()))
return Err(NokhwaError::DecoderInvalidConfiguration(
"Failed to convert frameformat to id".to_string(),
));
}
av_codec_params.profile = self.profile;
@@ -569,7 +711,10 @@ impl FfmpegDecoderConfig {
av_codec_params.sample_aspect_ratio = self.sample_aspect_ratio;
av_codec_params.framerate = AVRational { num: self.frame_rate.numerator(), den: self.frame_rate.numerator() };
av_codec_params.framerate = AVRational {
num: self.frame_rate.numerator(),
den: self.frame_rate.numerator(),
};
av_codec_params.field_order = self.field_order;
@@ -597,18 +742,18 @@ impl FfmpegDecoderConfig {
impl From<CameraFormat> for FfmpegDecoderConfig {
fn from(value: CameraFormat) -> Self {
Self {
frame_format: *value.format(),
frame_format: value.format(),
codec_tag: 0,
extra_data: None,
extra_data_size: 0,
coded_side_data: None,
coded_side_data_size: 0,
// extra_data: None,
// extra_data_size: 0,
// coded_side_data: None,
// coded_side_data_size: 0,
pix_fmt: None,
profile: 0,
level: 0,
resolution: *value.resolution(),
resolution: value.resolution(),
sample_aspect_ratio: AVRational { num: 0, den: 1 },
frame_rate: *value.frame_rate(),
frame_rate: value.frame_rate(),
field_order: AVFieldOrder::AV_FIELD_UNKNOWN,
color_range: AVColorRange::AVCOL_RANGE_UNSPECIFIED,
color_primaries: AVColorPrimaries::AVCOL_PRI_RESERVED0,
View File
+6
View File
@@ -1,2 +1,8 @@
#[cfg(feature = "ffmpeg")]
pub mod ffmpeg;
#[cfg(feature = "general")]
pub mod general;
#[cfg(feature = "mjpeg")]
pub mod mjpeg;
#[cfg(feature = "yuyv")]
pub mod yuv;
+199
View File
@@ -0,0 +1,199 @@
use zune_core::bytestream::ZCursor;
use zune_core::colorspace::ColorSpace;
pub use zune_core::options::DecoderOptions;
use zune_jpeg::errors::DecodeErrors;
pub use zune_jpeg::ImageInfo;
use zune_jpeg::JpegDecoder;
use nokhwa_core::codec::Codec;
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
use nokhwa_core::types::Resolution;
#[derive(Clone, Debug)]
pub struct MJpegDecoder {
config: MJpegOptions,
}
impl Decoder for MJpegDecoder {
type Config = MJpegOptions;
type OutputMeta = ImageMeta;
type DestinationFormatHint = OutputColor;
fn config(&self) -> &Self::Config {
&self.config
}
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
self.config = config;
Ok(())
}
fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option<Self::DestinationFormatHint>) -> Result<Self::OutputMeta, NokhwaError> {
let buffer = buffer.as_mut();
let cursor = ZCursor::new(to_decode.as_ref());
let mut decoder = JpegDecoder::new(cursor);
let mut config = self.config.decoder_options;
if let Some(dest_hint) = destination_format_hint {
config = self.config.decoder_options.jpeg_set_out_colorspace(dest_hint.into());
}
decoder.set_options(config);
decoder.decode_into(buffer).map_err(err_to_err)?;
let info = match decoder.info() {
Some(i) => i,
None => return Err(NokhwaError::Decoder("??????? how did we get here".to_string()))
};
Ok(info.into())
}
fn decode_to_pixel_buffer<P: Pixel>(&mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth
{
let hint = match pixel_to_colorspace::<P>() {
Some(cs) => cs,
None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES))
};
let meta = self.decode_to_buffer(to_decode, buffer, Some(hint))?;
Ok(meta)
}
fn decode<P: Pixel>(&mut self, to_decode: FrameBuffer) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth
{
let min_size = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
let mut buffer = vec![0_u8; min_size];
let output_metadata = self.decode_to_pixel_buffer(to_decode, buffer.as_mut_slice())?;
let image_buffer = ImageBuffer::from_raw(output_metadata.resolution.width(), output_metadata.resolution.height(), buffer).ok_or(NokhwaError::Decoder("Failed to make imagebuffer".to_string()))?;
Ok(DecodedImage::new(
image_buffer,
output_metadata
))
}
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
let stride = match destination_format {
OutputColor::Rgb => 3,
OutputColor::RgbA => 4,
OutputColor::Bgr => 3,
OutputColor::BgrA => 4,
OutputColor::Luma => 1,
OutputColor::LumaA => 2,
};
(resolution.width() * resolution.height() * stride) as usize
}
fn buffer_takes_destination_hint(&self) -> bool {
true
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ImageMeta {
pub resolution: Resolution,
pub pixel_density: u8,
pub x_density: u16,
pub y_density: u16,
pub components: u8,
pub multi_picture_information: Option<Vec<u8>>,
}
impl From<ImageInfo> for ImageMeta {
fn from(value: ImageInfo) -> Self {
Self {
resolution: Resolution::new(value.width as u32, value.width as u32),
pixel_density: value.pixel_density,
x_density: value.x_density,
y_density: value.y_density,
components: value.components,
multi_picture_information: value.multi_picture_information,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct MJpegOptions {
pub resolution: Resolution,
pub decoder_options: DecoderOptions
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum OutputColor {
Rgb,
RgbA,
Bgr,
BgrA,
Luma,
LumaA,
}
impl Into<ColorSpace> for OutputColor {
fn into(self) -> ColorSpace {
match self {
OutputColor::Rgb => ColorSpace::RGB,
OutputColor::RgbA => ColorSpace::RGBA,
OutputColor::Bgr => ColorSpace::BGR,
OutputColor::BgrA => ColorSpace::BGRA,
OutputColor::Luma => ColorSpace::Luma,
OutputColor::LumaA => ColorSpace::LumaA,
}
}
}
fn pixel_to_colorspace<P>() -> Option<OutputColor> where P: Pixel, <P as Pixel>::Subpixel: NonFloatScalarWidth {
match P::COLOR_MODEL {
"RGBA" => Some(OutputColor::RgbA),
"RGB" => Some(OutputColor::Rgb),
"BGR" => Some(OutputColor::Bgr),
"BGRA" => Some(OutputColor::BgrA),
"Y" => Some(OutputColor::Luma),
"YA" => Some(OutputColor::LumaA),
_ => None,
}
}
fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError {
match decode_errors {
DecodeErrors::Format(fmt) => {
NokhwaError::Decoder(fmt)
}
DecodeErrors::FormatStatic(fmt) => {
NokhwaError::Decoder(fmt.to_string())
}
DecodeErrors::IllegalMagicBytes(b) => {
NokhwaError::DecoderInvalidFrameData(format!("bad magic bytes: {b}"))
}
DecodeErrors::HuffmanDecode(huff) => {
NokhwaError::DecoderInvalidFrameData(format!("bad huffman tables: {huff}"))
}
DecodeErrors::ZeroError => {
NokhwaError::DecoderInvalidBuffer("image has zero width.".to_string())
}
DecodeErrors::DqtError(e) | DecodeErrors::MCUError(e) | DecodeErrors::SosError(e) | DecodeErrors::SofError(e)=> {
NokhwaError::Decoder(format!("error decoding: {e}"))
}
DecodeErrors::Unsupported(why) => {
NokhwaError::DecoderInvalidConfiguration(format!("not supported: {why:?}"))
}
DecodeErrors::ExhaustedData => {
NokhwaError::DecoderInvalidBuffer("image data exhausted".to_string())
}
DecodeErrors::LargeDimensions(size) => {
NokhwaError::DecoderInvalidBuffer(format!("too large: {size}"))
}
DecodeErrors::TooSmallOutput(a, b) => {
NokhwaError::DecoderInvalidBuffer(format!("too small: {a},{b}"))
}
DecodeErrors::IoErrors(io) => {
NokhwaError::Decoder(format!("io error: {io:?}"))
}
}
}
+471
View File
@@ -0,0 +1,471 @@
use bytemuck::{cast_slice, cast_slice_mut, try_cast_slice_mut};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::frame_format::FrameFormat;
use nokhwa_core::types::{CameraFormat, Resolution};
use yuv::{ayuv_to_rgb, ayuv_to_rgba, p010_to_bgr, p010_to_bgra, p010_to_rgb, p010_to_rgb10, p010_to_rgba, p010_to_rgba10, p012_to_rgb12, p012_to_rgba12, uyvy422_to_bgr, uyvy422_to_bgra, uyvy422_to_rgb, uyvy422_to_rgb_p16, uyvy422_to_rgba, uyvy422_to_rgba_p16, vyuy422_to_bgr, vyuy422_to_bgra, vyuy422_to_rgb, vyuy422_to_rgb_p16, vyuy422_to_rgba, vyuy422_to_rgba_p16, yuv420_to_bgr, yuv420_to_bgra, yuv420_to_rgb, yuv420_to_rgba, yuv_nv12_to_bgr, yuv_nv12_to_bgra, yuv_nv12_to_rgb, yuv_nv12_to_rgba, yuv_nv16_to_bgr, yuv_nv16_to_bgra, yuv_nv16_to_rgb, yuv_nv16_to_rgba, yuv_nv21_to_bgr, yuv_nv21_to_bgra, yuv_nv21_to_rgb, yuv_nv21_to_rgba, yuv_nv24_to_bgr, yuv_nv24_to_bgra, yuv_nv24_to_rgb, yuv_nv24_to_rgba, yuv_nv42_to_bgr, yuv_nv42_to_bgra, yuv_nv42_to_rgb, yuv_nv42_to_rgba, yuv_nv61_to_bgr, yuv_nv61_to_bgra, yuv_nv61_to_rgb, yuv_nv61_to_rgba, yuyv422_to_bgr, yuyv422_to_bgra, yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange, YuvStandardMatrix};
use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel, Primitive};
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
pub struct YUVDecoder {
config: YUVConfig,
stride_cache: Option<CachedStride>,
}
impl Decoder for YUVDecoder {
type Config = YUVConfig;
type OutputMeta = ();
type DestinationFormatHint = YUVDestination;
fn config(&self) -> &Self::Config {
&self.config
}
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError> {
if !FrameFormat::YCBCR.contains(&config.yuv_type) {
return Err(NokhwaError::DecoderUnsupportedFrameFormat(config.yuv_type))
}
self.config = config;
self.stride_cache = None;
Ok(())
}
fn decode_to_buffer(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, destination_format: Option<Self::DestinationFormatHint>) -> Result<Self::OutputMeta, NokhwaError> {
let destination_format = match destination_format {
Some(df) => df,
None => return Err(NokhwaError::DecoderDestinationHintRequired)
};
let buffer = buffer.as_mut();
if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format) {
return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string()))
}
let stride = match self.stride_cache {
Some(c) => c,
None => {
self.stride_cache = figure_out_stride(self.config.yuv_type);
match self.stride_cache {
Some(s) => s,
None => return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)),
}
}
};
// todo: clean up ts into a macro </3
let decode_status = match stride {
CachedStride::Packed(stride) => {
let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride);
match self.config.yuv_type {
FrameFormat::Ayuv_32 => {
match destination_format {
YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
_ => None,
}
}
FrameFormat::Yuyv_4_2_2 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, 8, self.config.range, self.config.matrix)),
YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
}
}
FrameFormat::Uyvy_4_2_2 => {
match destination_format {
YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
}
}
FrameFormat::Vyuy_4_2_2 => {
match destination_format {
YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
}
}
FrameFormat::Yvyu_4_2_2 => {
match destination_format {
YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), 6, 16, self.config.range, self.config.matrix)),
YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
}
}
_ => {
if FrameFormat::YCBCR_PACKED.contains(&self.config.yuv_type) {
return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string()))
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))
}
}
}
CachedStride::SemiPlanar(y_stride, uv_stride) => {
let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, y_stride, uv_stride);
match self.config.yuv_type {
FrameFormat::NV24 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::NV42 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::NV16 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::NV61 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::NV12 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::NV21 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
_ => None,
}
}
FrameFormat::P010 => {
match destination_format {
YUVDestination::Rgb8 => Some(p010_to_rgb(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgba8 => Some(p010_to_rgba(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgr8 => Some(p010_to_bgr(&convert_bi_planar_image_to_u16(image), buffer, 3, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Bgra8 => Some(p010_to_bgra(&convert_bi_planar_image_to_u16(image), buffer, 4, self.config.range, self.config.matrix, self.config.mode)),
YUVDestination::Rgb16 => Some(p010_to_rgb10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(p010_to_rgba10(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)),
// _ => None,
}
}
FrameFormat::P012 => {
match destination_format {
YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 6, self.config.range, self.config.matrix)),
YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), 8, self.config.range, self.config.matrix)),
_ => None,
}
}
_ => {
if FrameFormat::YCBCR_SEMI.contains(&self.config.yuv_type) {
return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string()))
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))
}
}
}
CachedStride::Planar(y_stride, u_stride, v_stride) => {
let image = prepare_to_planar_image(&to_decode, self.config.resolution, y_stride, u_stride, v_stride);
match self.config.yuv_type {
FrameFormat::Yuv_4_2_0 => {
match destination_format {
YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, 4, self.config.range, self.config.matrix)),
YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, 3, self.config.range, self.config.matrix)),
YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, 4, self.config.range, self.config.matrix)),
_ => None,
}
}
_ => {
if FrameFormat::YCBCR_PLANAR.contains(&self.config.yuv_type) {
return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string()))
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type))
}
}
}
};
match decode_status {
Some(Ok(_)) => Ok(()),
Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())),
None => Err(NokhwaError::DecoderUnsupportedFrameFormat(self.config.yuv_type)),
}
}
fn decode_to_pixel_buffer<P: Pixel>(&mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth
{
let destination = match YUVDestination::get_by_pixel::<P>() {
None => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(P::COLOR_MODEL, <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES)),
Some(d) => d,
};
let buffer = buffer.as_mut();
let cast_slice = try_cast_slice_mut::<P::Subpixel, u8>(buffer)
.map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?;
self.decode_to_buffer(to_decode, cast_slice, Some(destination))
}
fn decode<P: Pixel>(&mut self, to_decode: FrameBuffer<'_>) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth
{
let min_size_alloc = self.output_decoder_min_size_pixel::<P>(self.config.resolution);
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc];
self.decode_to_pixel_buffer::<P>(to_decode, &mut out_buffer)?;
Ok(
DecodedImage::new(
ImageBuffer::from_vec(self.config.resolution.width(), self.config.resolution.height(), out_buffer)
.ok_or(NokhwaError::Decoder("failed to convert into an image buffer".to_string()))?,
()
)
)
}
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize {
let px_size = match destination_format {
YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3,
YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4,
YUVDestination::Rgb16 => 3 * 2,
YUVDestination::Rgba16 => 4 * 2,
};
let reso = resolution.width() * resolution.height();
(reso as usize) * (px_size as usize)
}
fn buffer_takes_destination_hint(&self) -> bool {
true
}
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum YUVDestination {
Rgb8,
Rgba8,
Rgb16,
Rgba16,
Bgr8,
Bgra8,
}
impl YUVDestination {
pub fn get_by_pixel<P>() -> Option<Self> where P: Pixel, <P as Pixel>::Subpixel: NonFloatScalarWidth {
match P::COLOR_MODEL {
"RGB" => {
match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
1 => Some(YUVDestination::Rgb8),
2 => Some(YUVDestination::Rgb16),
_ => None,
}
}
"RGBA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
1 => Some(YUVDestination::Rgba8),
2 => Some(YUVDestination::Rgba16),
_ => None,
}
"BGR" =>match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
1 => Some(YUVDestination::Bgr8),
// 2 => Some(YUVDestination::Bgr16),
_ => None,
}
"BGRA" => match <<P as Pixel>::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES {
1 => Some(YUVDestination::Bgra8),
// 2 => Some(YUVDestination::Bgra16),
_ => None,
}
_ => None,
}
}
}
#[derive(Clone, Debug, PartialOrd, PartialEq)]
pub struct YUVConfig {
pub resolution: Resolution,
pub yuv_type: FrameFormat,
pub range: YuvRange,
pub matrix: YuvStandardMatrix,
pub mode: YuvConversionMode,
pub premultiply_alpha: bool,
}
impl TryFrom<CameraFormat> for YUVConfig {
type Error = NokhwaError;
fn try_from(value: CameraFormat) -> Result<Self, Self::Error> {
if !FrameFormat::YCBCR.contains(&value.format()) {
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()))
}
Ok(YUVConfig {
resolution: value.resolution(),
yuv_type: value.format(),
range: YuvRange::Full,
matrix: YuvStandardMatrix::Bt601,
mode: YuvConversionMode::Balanced,
premultiply_alpha: false,
})
}
}
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
enum CachedStride {
Packed(u32),
SemiPlanar(u32, u32),
Planar(u32, u32, u32),
}
fn figure_out_stride(frame_format: FrameFormat) -> Option<CachedStride> {
if let Some(s) = packed_stride_component_per_4px(frame_format) {
return Some(CachedStride::Packed(s));
}
if let Some((s1, s2)) = semiplanar_stride_per_4px(frame_format) {
return Some(CachedStride::SemiPlanar(s1, s2));
}
if let Some((s1, s2, s3)) = planar_stride_per_4px(frame_format) {
return Some(CachedStride::Planar(s1, s2, s3));
}
None
}
fn packed_stride_component_per_4px(format: FrameFormat) -> Option<u32> {
match format {
FrameFormat::Ayuv_32 => Some(64),
FrameFormat::Yuyv_4_2_2
| FrameFormat::Uyvy_4_2_2
| FrameFormat::Vyuy_4_2_2
| FrameFormat::Yvyu_4_2_2 => Some(16),
_ => None,
}
}
fn prepare_to_packed_image<'a>(
frame_buffer: &'a FrameBuffer<'a>,
resolution: Resolution,
yuy_stride: u32,
) -> YuvPackedImage<'a, u8> {
YuvPackedImage {
yuy: frame_buffer.buffer(),
yuy_stride,
width: resolution.width(),
height: resolution.height(),
}
}
fn semiplanar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32)> {
match format {
FrameFormat::NV24 | FrameFormat::NV42 => Some((4, 8)),
FrameFormat::NV16 | FrameFormat::NV61 => Some((4, 4)),
FrameFormat::NV12 | FrameFormat::NV21 | FrameFormat::P010 | FrameFormat::P012 => {
Some((4, 4))
}
_ => None,
}
}
fn prepare_to_semi_planar_image<'a>(
frame_buffer: &'a FrameBuffer<'a>,
resolution: Resolution,
y_stride: u32,
uv_stride: u32,
) -> YuvBiPlanarImage<'a, u8> {
let uv_start = (resolution.width() * resolution.height()) as usize;
YuvBiPlanarImage {
y_plane: &frame_buffer.buffer()[0..uv_start],
y_stride,
uv_plane: &frame_buffer.buffer()[uv_start..frame_buffer.len()],
uv_stride,
width: resolution.width(),
height: resolution.height(),
}
}
fn planar_stride_per_4px(format: FrameFormat) -> Option<(u32, u32, u32)> {
match format {
FrameFormat::Yuv_4_2_0 => Some((4, 1, 1)),
_ => None,
}
}
fn prepare_to_planar_image<'a>(
frame_buffer: &'a FrameBuffer<'a>,
resolution: Resolution,
y_stride: u32,
u_stride: u32,
v_stride: u32,
) -> YuvPlanarImage<'a, u8> {
let u_start = (resolution.width() * resolution.height()) as usize;
let v_start =
u_start + ((resolution.width() / 4) * y_stride * (resolution.height() * u_stride)) as usize;
YuvPlanarImage {
y_plane: &frame_buffer.buffer()[0..u_start],
y_stride,
u_plane: &frame_buffer.buffer()[u_start..v_start],
u_stride,
v_plane: &frame_buffer.buffer()[v_start..frame_buffer.len()],
v_stride,
width: resolution.width(),
height: resolution.height(),
}
}
fn convert_packed_image_to_u16(
yuv_packed_image: YuvPackedImage<u8>,
) -> YuvPackedImage<u16> {
let buf= cast_slice(yuv_packed_image.yuy);
YuvPackedImage {
yuy: buf,
yuy_stride: yuv_packed_image.yuy_stride,
width: yuv_packed_image.width,
height: yuv_packed_image.height,
}
}
fn convert_bi_planar_image_to_u16(
yuv_bi_planar_image: YuvBiPlanarImage<u8>,
) -> YuvBiPlanarImage<u16> {
let buf_y= cast_slice(yuv_bi_planar_image.y_plane);
let buf_uv= cast_slice(yuv_bi_planar_image.uv_plane);
YuvBiPlanarImage {
y_plane: buf_y,
y_stride: yuv_bi_planar_image.y_stride,
uv_plane: buf_uv,
uv_stride: yuv_bi_planar_image.uv_stride,
width: yuv_bi_planar_image.width,
height: yuv_bi_planar_image.height,
}
}