use std::error::Error;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
bindings::{
self, NDIlib_frame_format_type_e, NDIlib_recv_bandwidth_e, NDIlib_recv_color_format_e,
},
four_cc::FourCCVideo,
};
#[cfg_attr(target_os = "windows", repr(i32))]
#[cfg_attr(not(target_os = "windows"), repr(u32))]
#[non_exhaustive]
#[allow(non_camel_case_types)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
pub enum NDIPreferredColorFormat {
#[default]
Fastest = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_fastest,
Best = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_best,
BGRX_BGRA = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_BGRX_BGRA,
UYVY_BGRA = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_UYVY_BGRA,
RGBX_RGBA = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_RGBX_RGBA,
UYVY_RGBA = bindings::NDIlib_recv_color_format_e_NDIlib_recv_color_format_UYVY_RGBA,
}
impl NDIPreferredColorFormat {
pub(crate) fn to_ffi(self) -> NDIlib_recv_color_format_e {
self.into()
}
#[expect(unused)]
pub(crate) fn from_ffi(value: NDIlib_recv_color_format_e) -> Option<Self> {
Self::try_from_primitive(value).ok()
}
pub const fn without_alpha_four_cc(self) -> Option<FourCCVideo> {
match self {
NDIPreferredColorFormat::BGRX_BGRA => Some(FourCCVideo::BGRX),
NDIPreferredColorFormat::RGBX_RGBA => Some(FourCCVideo::RGBX),
NDIPreferredColorFormat::UYVY_BGRA | NDIPreferredColorFormat::UYVY_RGBA => {
Some(FourCCVideo::UYVY)
}
_ => None,
}
}
pub const fn with_alpha_four_cc(self) -> Option<FourCCVideo> {
match self {
NDIPreferredColorFormat::BGRX_BGRA | NDIPreferredColorFormat::UYVY_BGRA => {
Some(FourCCVideo::BGRA)
}
NDIPreferredColorFormat::RGBX_RGBA | NDIPreferredColorFormat::UYVY_RGBA => {
Some(FourCCVideo::RGBA)
}
_ => None,
}
}
pub fn from_four_cc(
with_alpha: Option<FourCCVideo>,
without_alpha: Option<FourCCVideo>,
) -> Result<Self, FromFourCCError> {
use FourCCVideo::*;
use NDIPreferredColorFormat::*;
if let Some(format) = with_alpha {
match format {
BGRA | RGBA => {}
BGRX | RGBX | UYVY => {
return Err(FromFourCCError::WrongAlphaMode {
format,
has_alpha: false,
expected_alpha: true,
});
}
format => {
return Err(FromFourCCError::UnsupportedFormat { format });
}
}
}
if let Some(format) = without_alpha {
match format {
BGRX | RGBX | UYVY => {}
BGRA | RGBA => {
return Err(FromFourCCError::WrongAlphaMode {
format,
has_alpha: true,
expected_alpha: false,
});
}
format => {
return Err(FromFourCCError::UnsupportedFormat { format });
}
}
}
match (without_alpha, with_alpha) {
(Some(BGRX), Some(BGRA)) => Ok(BGRX_BGRA),
(Some(RGBX), Some(RGBA)) => Ok(RGBX_RGBA),
(Some(UYVY), Some(BGRA)) => Ok(UYVY_BGRA),
(Some(UYVY), Some(RGBA)) => Ok(UYVY_RGBA),
(Some(BGRX), None) => Ok(BGRX_BGRA),
(Some(RGBX), None) => Ok(RGBX_RGBA),
(Some(UYVY), None) => Ok(UYVY_BGRA),
(None, Some(BGRA)) => Ok(BGRX_BGRA),
(None, Some(RGBA)) => Ok(RGBX_RGBA),
(None, None) => Ok(Fastest),
_ => Err(FromFourCCError::UnsupportedCombination),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FromFourCCError {
UnsupportedFormat {
format: FourCCVideo,
},
WrongAlphaMode {
format: FourCCVideo,
has_alpha: bool,
expected_alpha: bool,
},
UnsupportedCombination,
}
impl std::fmt::Display for FromFourCCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FromFourCCError::UnsupportedFormat { format } => {
write!(f, "Unsupported video format {format:?}")
}
FromFourCCError::WrongAlphaMode {
format,
has_alpha,
expected_alpha,
} => write!(
f,
"Wrong alpha mode in {format:?}, has {}alpha but expected {}alpha",
if *has_alpha { "" } else { "no " },
if *expected_alpha { "" } else { "no " }
),
FromFourCCError::UnsupportedCombination => {
f.write_str("Unsupported format combination")
}
}
}
}
impl Error for FromFourCCError {}
#[repr(i32)]
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
pub enum NDIBandwidthMode {
#[default]
Default = bindings::NDIlib_recv_bandwidth_e_NDIlib_recv_bandwidth_highest,
Preview = bindings::NDIlib_recv_bandwidth_e_NDIlib_recv_bandwidth_lowest,
AudioOnly = bindings::NDIlib_recv_bandwidth_e_NDIlib_recv_bandwidth_audio_only,
MetadataOnly = bindings::NDIlib_recv_bandwidth_e_NDIlib_recv_bandwidth_metadata_only,
}
impl NDIBandwidthMode {
pub(crate) fn to_ffi(self) -> NDIlib_recv_bandwidth_e {
self.into()
}
#[expect(unused)]
pub(crate) fn from_ffi(value: NDIlib_recv_bandwidth_e) -> Option<Self> {
Self::try_from_primitive(value).ok()
}
}
#[cfg_attr(target_os = "windows", repr(i32))]
#[cfg_attr(not(target_os = "windows"), repr(u32))]
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
pub enum NDIFieldedFrameMode {
#[default]
Progressive = bindings::NDIlib_frame_format_type_e_NDIlib_frame_format_type_progressive,
Interleaved = bindings::NDIlib_frame_format_type_e_NDIlib_frame_format_type_interleaved,
Field0 = bindings::NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_0,
Field1 = bindings::NDIlib_frame_format_type_e_NDIlib_frame_format_type_field_1,
}
impl NDIFieldedFrameMode {
pub(crate) fn to_ffi(self) -> NDIlib_frame_format_type_e {
self.into()
}
pub(crate) fn from_ffi(value: NDIlib_frame_format_type_e) -> Option<Self> {
Self::try_from_primitive(value).ok()
}
pub const fn is_progressive(self) -> bool {
matches!(self, NDIFieldedFrameMode::Progressive)
}
pub const fn is_fielded(self) -> bool {
!self.is_progressive()
}
pub const fn is_single_field(self) -> bool {
matches!(
self,
NDIFieldedFrameMode::Field0 | NDIFieldedFrameMode::Field1
)
}
}
#[must_use]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum NDIRecvType {
Video,
Audio,
Metadata,
None,
StatusChange,
SourceChange,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum NDIRecvError {
UnknownType,
NotWritable,
}
impl std::fmt::Display for NDIRecvError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnknownType => f.write_str("Unknown frame type returned from SDK"),
Self::NotWritable => f.write_str("Frame is not writable"),
}
}
}
impl Error for NDIRecvError {}