mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
fix core compile errors, yuv and mjpeg decoder implementation TODO write tests
This commit is contained in:
Generated
+6
-6
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
|
||||
+14
-12
@@ -9,41 +9,42 @@ use std::sync::Arc;
|
||||
|
||||
pub trait Setting {
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
/// Will error on
|
||||
fn enumerate_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
/// Will error on
|
||||
fn enumerate_resolution_and_frame_rates(
|
||||
&self,
|
||||
frame_format: FrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
/// 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>;
|
||||
|
||||
fn control_description(&self, id: &ControlId) -> Option<&ControlDescription>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
/// Will error on
|
||||
fn set_control(&mut self, property: &ControlId, value: ControlValue)
|
||||
-> Result<(), NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Will error on
|
||||
/// Will error on
|
||||
fn refresh_controls(&mut self) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(feature = "async", async_trait::async_trait)]
|
||||
pub trait AsyncSetting {
|
||||
async fn enumerate_formats_async(&self) -> Result<Vec<CameraFormat>, NokhwaError>;
|
||||
|
||||
@@ -53,7 +54,7 @@ pub trait AsyncSetting {
|
||||
) -> Result<HashMap<Resolution, Vec<FrameRate>>, NokhwaError>;
|
||||
|
||||
async fn set_format_async(&self, camera_format: CameraFormat) -> Result<(), NokhwaError>;
|
||||
|
||||
|
||||
async fn set_property_async(
|
||||
&mut self,
|
||||
property: &ControlId,
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
+10
-10
@@ -8,27 +8,27 @@ pub trait Codec {
|
||||
|
||||
type Input<'a>;
|
||||
|
||||
type Output;
|
||||
type Output<'a>;
|
||||
|
||||
type WrittenMeta: Clone + Debug;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
/// Errors are decoder specific.
|
||||
fn allowed_formats(&self) -> Result<&[FrameFormat], NokhwaError>;
|
||||
|
||||
fn config(&self) -> &Self::Config;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
/// Errors are decoder specific.
|
||||
fn set_config(&mut self, config: Self::Config) -> Result<(), NokhwaError>;
|
||||
|
||||
/// # Errors
|
||||
/// Errors are decoder specific.
|
||||
/// Errors are decoder specific.
|
||||
fn send_item(&mut self, input: Self::Input<'_>) -> Result<(), NokhwaError>;
|
||||
|
||||
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> {
|
||||
|
||||
+59
-39
@@ -61,15 +61,16 @@ 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) {
|
||||
return None;
|
||||
}
|
||||
if let Some(description) = device_controls.get(id)
|
||||
&& !description.validate(value)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -119,11 +127,11 @@ impl Controls {
|
||||
value: &ControlValue,
|
||||
) -> Result<bool, NokhwaError> {
|
||||
let Some(description) = self.descriptions.get(control_id) else {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
});
|
||||
};
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: control_id.to_string(),
|
||||
error: "ID Not Found".to_string(),
|
||||
});
|
||||
};
|
||||
|
||||
if !self.values.contains_key(control_id) {
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
@@ -163,15 +171,16 @@ 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) {
|
||||
return None;
|
||||
}
|
||||
if let Some(default) = &default_value
|
||||
&& !control_value_descriptor.validate(default)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
@@ -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,16 +357,20 @@ pub enum ControlValue {
|
||||
}
|
||||
|
||||
impl ControlValue {
|
||||
#[must_use] pub fn is_primitive(&self) -> bool {
|
||||
matches!(self, ControlValue::Null
|
||||
| ControlValue::Integer(_)
|
||||
| ControlValue::BitMask(_)
|
||||
| ControlValue::Float(_)
|
||||
| ControlValue::String(_)
|
||||
| ControlValue::Boolean(_)
|
||||
| ControlValue::Binary(_)
|
||||
| ControlValue::Area { .. }
|
||||
| ControlValue::Orientation(_))
|
||||
#[must_use]
|
||||
pub fn is_primitive(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ControlValue::Null
|
||||
| ControlValue::Integer(_)
|
||||
| ControlValue::BitMask(_)
|
||||
| ControlValue::Float(_)
|
||||
| ControlValue::String(_)
|
||||
| ControlValue::Boolean(_)
|
||||
| ControlValue::Binary(_)
|
||||
| ControlValue::Area { .. }
|
||||
| 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 {
|
||||
|
||||
@@ -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,18 +18,34 @@ 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>(
|
||||
&mut self,
|
||||
to_decode: FrameBuffer,
|
||||
buffer: impl AsMut<[P::Subpixel]>,
|
||||
) -> Result<Self::OutputMeta, NokhwaError>
|
||||
where <P as Pixel>::Subpixel: NonFloatScalarWidth;
|
||||
) -> Result<Self::OutputMeta, NokhwaError>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
|
||||
@@ -124,7 +127,7 @@ impl FormatRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[must_use]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn format_distance_to_point(
|
||||
resolution: &Option<Resolution>,
|
||||
@@ -132,13 +135,13 @@ 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(),
|
||||
None => 0_f32,
|
||||
};
|
||||
|
||||
|
||||
let resolution_point_distance = match resolution {
|
||||
Some(res_pt) => format.resolution().distance_from(res_pt) as f32,
|
||||
None => 0_f32,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+152
-285
@@ -14,75 +14,22 @@
|
||||
* 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 => [
|
||||
$($format:ident),* $(,)?
|
||||
$classifier:ident [
|
||||
$(
|
||||
$sub_classifier:ident [
|
||||
$(
|
||||
$group_name:expr => [
|
||||
$($format:ident),* $(,)?
|
||||
]
|
||||
),* $(,)?
|
||||
]
|
||||
),* $(,)?
|
||||
]
|
||||
),* $(,)?
|
||||
) => {
|
||||
@@ -98,247 +45,166 @@ macro_rules! define_frame_format_with_groups {
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum FrameFormat {
|
||||
$(
|
||||
$($format,)*
|
||||
$(
|
||||
$($($format,)*)*
|
||||
)*
|
||||
)*
|
||||
Custom(CustomFrameFormat),
|
||||
}
|
||||
|
||||
impl FrameFormat {
|
||||
paste::paste! {
|
||||
impl FrameFormat {
|
||||
$(
|
||||
pub const $group_name: &'static [FrameFormat] = &[
|
||||
$(FrameFormat::$format),*
|
||||
];
|
||||
)*
|
||||
pub const ALL: &'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,)*)*
|
||||
)*)*
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_frame_format_with_groups! {
|
||||
COMPRESSED => [
|
||||
H265,
|
||||
HEVC,
|
||||
H264,
|
||||
AVC1,
|
||||
H263,
|
||||
AV1,
|
||||
MPEG_1,
|
||||
MPEG_2,
|
||||
MPEG_4,
|
||||
MJPEG,
|
||||
XviD,
|
||||
VP8,
|
||||
VP9,
|
||||
COMPRESSED [
|
||||
MPEG [
|
||||
H => [
|
||||
H265,
|
||||
HEVC,
|
||||
H264,
|
||||
H263,
|
||||
],
|
||||
MPEG4 => [
|
||||
AVC1,
|
||||
MPEG_4,
|
||||
XviD,
|
||||
],
|
||||
MPEG => [
|
||||
MPEG_1,
|
||||
MPEG_2,
|
||||
],
|
||||
],
|
||||
IMAGE [
|
||||
MJPEG => [
|
||||
MJPEG,
|
||||
]
|
||||
],
|
||||
OPEN [
|
||||
AOM => [
|
||||
AV1
|
||||
],
|
||||
WEB => [
|
||||
VP8,
|
||||
VP9
|
||||
],
|
||||
]
|
||||
],
|
||||
|
||||
YCBCR_PACKED_444 => [
|
||||
Ayuv_32,
|
||||
],
|
||||
YCBCR_PLANAR_444 => [],
|
||||
YCBCR_SEMI_PLANAR_444 => [
|
||||
NV24,
|
||||
NV42,
|
||||
],
|
||||
YCBCR_PACKED_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 => [
|
||||
NV16,
|
||||
NV61,
|
||||
],
|
||||
YCBCR_PACKED_420 => [],
|
||||
YCBCR_PLANAR_420 => [
|
||||
Yuv_4_2_0,
|
||||
Yvu_4_2_0,
|
||||
],
|
||||
YCBCR_SEMI_PLANAR_420 => [
|
||||
NV12,
|
||||
NV21,
|
||||
P010,
|
||||
P012,
|
||||
|
||||
],
|
||||
YCBCR_PACKED_411 => [
|
||||
Y41Packed
|
||||
],
|
||||
YCBCR_PLANAR_411 => [
|
||||
Y411Planar
|
||||
],
|
||||
YCBCR_SEMI_PLANAR_411 => [
|
||||
NV11,
|
||||
YCBCR [
|
||||
PACKED [
|
||||
444 => [
|
||||
Ayuv_32,
|
||||
],
|
||||
422 => [
|
||||
Yuyv_4_2_2,
|
||||
Uyvy_4_2_2,
|
||||
Vyuy_4_2_2,
|
||||
Yvyu_4_2_2,
|
||||
],
|
||||
420 => [],
|
||||
411 => [
|
||||
]
|
||||
],
|
||||
PLANAR [
|
||||
444 => [],
|
||||
422 => [],
|
||||
420 => [
|
||||
Yuv_4_2_0,
|
||||
],
|
||||
411 => [
|
||||
]
|
||||
],
|
||||
SEMI [
|
||||
444 => [
|
||||
NV24,
|
||||
NV42,
|
||||
],
|
||||
422 => [
|
||||
NV16,
|
||||
NV61,
|
||||
],
|
||||
420 => [
|
||||
NV12,
|
||||
NV21,
|
||||
P010,
|
||||
P012,
|
||||
],
|
||||
411 => [
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
LUMA => [
|
||||
Luma_8,
|
||||
Luma_10,
|
||||
Luma_12,
|
||||
Luma_14,
|
||||
Luma_16,
|
||||
Depth_16,
|
||||
BRIGHTNESS [
|
||||
LUMA [
|
||||
SMALL => [
|
||||
Luma_8,
|
||||
],
|
||||
LARGE => [
|
||||
Luma_10,
|
||||
Luma_12,
|
||||
Luma_14,
|
||||
Luma_16,
|
||||
],
|
||||
],
|
||||
DEPTH [
|
||||
SMALL => [],
|
||||
LARGE => [Depth_16],
|
||||
]
|
||||
],
|
||||
|
||||
RAW_RGB => [
|
||||
Rgb_3_3_2,
|
||||
Rgb_5_6_5,
|
||||
Rgb_5_5_5,
|
||||
Rgb_8_8_8,
|
||||
Argb_8_8_8_8,
|
||||
Rgba_8_8_8_8,
|
||||
],
|
||||
|
||||
RAW_BGR => [
|
||||
Bgr_3_3_2,
|
||||
Bgr_5_6_5,
|
||||
Bgr_5_5_5,
|
||||
Bgr_8_8_8,
|
||||
Abgr_8_8_8_8,
|
||||
Bgra_8_8_8_8,
|
||||
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,
|
||||
]
|
||||
],
|
||||
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]),
|
||||
|
||||
+20
-21
@@ -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
|
||||
@@ -52,27 +51,27 @@ where
|
||||
}
|
||||
|
||||
// not for safe work ;p
|
||||
// TODO: add more custom integer sizes, or break our dependence on image entirely and
|
||||
// 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 ],
|
||||
}
|
||||
|
||||
@@ -35,3 +35,4 @@ pub mod stream;
|
||||
pub mod traits;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
mod metadata;
|
||||
|
||||
@@ -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>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
'('
|
||||
|
||||
')'
|
||||
} else {
|
||||
'('
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +178,9 @@ macro_rules! impl_num {
|
||||
($($n:ty)*) => ($(
|
||||
impl RangeItem for $n {
|
||||
const ZERO: $n = 0;
|
||||
|
||||
|
||||
const MIN: $n = <$n>::MIN;
|
||||
|
||||
|
||||
const MAX: $n = <$n>::MAX;
|
||||
}
|
||||
)*)
|
||||
|
||||
+29
-23
@@ -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,19 +129,19 @@ 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 {
|
||||
TryRecvError::Empty => Event::NotReady,
|
||||
TryRecvError::Disconnected => Event::Closed,
|
||||
}),
|
||||
.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,
|
||||
}),
|
||||
};
|
||||
|
||||
if let Event::FormatChange(fmt) = event {
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
+322
-177
@@ -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,
|
||||
@@ -111,79 +160,135 @@ impl Decoder for FfmpegDecoder {
|
||||
});
|
||||
self.sws.as_mut().unwrap()
|
||||
}
|
||||
Some(v) => {
|
||||
if v.source_pixel_format != source_format || v.dest_pixel_format != destination_format {
|
||||
Some(v) => {
|
||||
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(), ¶m)
|
||||
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(),
|
||||
¶m,
|
||||
)
|
||||
};
|
||||
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."]
|
||||
@@ -529,24 +669,24 @@ impl FfmpegDecoderConfig {
|
||||
pub fn with_camera_format(camera_format: &CameraFormat) -> Self {
|
||||
FfmpegDecoderConfig::from(*camera_format)
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -584,10 +729,10 @@ impl FfmpegDecoderConfig {
|
||||
av_codec_params.chroma_location = self.chroma_location;
|
||||
|
||||
av_codec_params.video_delay = self.video_delay;
|
||||
|
||||
|
||||
Ok(av_codec_params)
|
||||
}
|
||||
|
||||
|
||||
/// SAFETY: the user is responsible for freeing this return value
|
||||
pub fn as_ptr(&self) -> Result<*mut AVCodecParameters, NokhwaError> {
|
||||
Ok(&mut self.as_avcodec_params()?)
|
||||
@@ -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,
|
||||
@@ -638,7 +783,7 @@ fn dealloc_av_params(avcodec_parameters: &mut Option<*mut AVCodecParameters>) {
|
||||
|
||||
impl Drop for FfmpegCodec {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.deinitialize();
|
||||
let _ = self.deinitialize();
|
||||
dealloc_av_params(&mut self.temp_cfg);
|
||||
unsafe {
|
||||
let mut ctx = self.decoder.as_mut_ptr();
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
#[cfg(feature = "ffmpeg")]
|
||||
pub mod ffmpeg;
|
||||
pub mod ffmpeg;
|
||||
#[cfg(feature = "general")]
|
||||
pub mod general;
|
||||
#[cfg(feature = "mjpeg")]
|
||||
pub mod mjpeg;
|
||||
#[cfg(feature = "yuyv")]
|
||||
pub mod yuv;
|
||||
|
||||
@@ -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:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user