1use std::fmt::{Debug, Display};
7
8use num_enum::{IntoPrimitive, TryFromPrimitive};
9
10use crate::{
11 bindings::{self, NDIlib_FourCC_audio_type_e, NDIlib_FourCC_video_type_e},
12 enums::NDIFieldedFrameMode,
13 buffer_info::BufferInfo, resolution::Resolution, subsampling::Subsampling,
14};
15
16#[repr(i32)]
18#[non_exhaustive]
19#[allow(non_camel_case_types)]
20#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
21pub enum FourCCVideo {
22 UYVY = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_UYVY,
23 UYVA = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_UYVA,
24 P216 = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_P216,
25 PA16 = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_PA16,
26 YV12 = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_YV12,
27 I420 = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_I420,
28 NV12 = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_NV12,
29 RGBA = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_RGBA,
31 RGBX = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_RGBX,
33 BGRA = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_BGRA,
35 BGRX = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_BGRX,
37}
38
39impl FourCCVideo {
40 pub(crate) fn to_ffi(self) -> NDIlib_FourCC_video_type_e {
41 self.into()
42 }
43
44 pub(crate) fn from_ffi(value: NDIlib_FourCC_video_type_e) -> Option<Self> {
45 Self::try_from_primitive(value).ok()
46 }
47
48 pub fn buffer_info(
50 self,
51 resolution: Resolution,
52 field_mode: NDIFieldedFrameMode,
53 ) -> Result<BufferInfo, BufferInfoError> {
54 use FourCCVideo::*;
55 let pixel_stride;
56 let mut subsampling = Subsampling::none();
57
58 match self {
59 UYVY => {
60 pixel_stride = 2;
61 subsampling = Subsampling::new(4, 2, 2);
62 }
63 BGRA | BGRX | RGBA | RGBX => {
64 pixel_stride = 4;
65 }
66 cc => return Err(BufferInfoError::UnsupportedFourCC(cc)),
67 };
68
69 let size = resolution.pixels() * pixel_stride;
70
71 Ok(BufferInfo {
72 size: if field_mode.is_single_field() {
73 size / 2
74 } else {
75 size
76 },
77 line_stride: resolution.x * pixel_stride,
78 resolution,
79 field_mode,
80 subsampling,
81 })
82 }
83}
84
85#[non_exhaustive]
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum BufferInfoError {
88 UnsupportedFourCC(FourCCVideo),
90 UnspecifiedFourCC,
91}
92
93#[repr(i32)]
95#[non_exhaustive]
96#[allow(non_camel_case_types)]
97#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
98pub enum FourCCAudio {
99 #[default]
101 FLTP = bindings::NDIlib_FourCC_audio_type_e_NDIlib_FourCC_audio_type_FLTP,
102}
103
104impl FourCCAudio {
105 pub fn to_ffi(self) -> NDIlib_FourCC_audio_type_e {
106 self.into()
107 }
108
109 pub fn from_ffi(value: NDIlib_FourCC_audio_type_e) -> Option<Self> {
110 Self::try_from_primitive(value).ok()
111 }
112}
113
114#[repr(transparent)]
116#[derive(Clone, Copy, Hash, PartialEq, Eq)]
117pub struct FourCC {
118 code: i32,
120}
121
122impl FourCC {
123 pub fn from_ffi(code: i32) -> Self {
125 FourCC { code }
126 }
127
128 pub fn to_ffi(self) -> i32 {
130 self.code
131 }
132
133 pub fn as_video(&self) -> Option<FourCCVideo> {
135 FourCCVideo::from_ffi(self.code)
136 }
137
138 pub fn as_audio(&self) -> Option<FourCCAudio> {
140 FourCCAudio::from_ffi(self.code)
141 }
142
143 #[allow(clippy::inherent_to_string_shadow_display)] pub fn to_string(&self) -> String {
146 let bytes: [u8; 4] = self.code.to_le_bytes();
147
148 String::from_utf8_lossy(&bytes).to_string()
149 }
150}
151
152impl Display for FourCC {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 let bytes: [u8; 4] = self.code.to_le_bytes();
155
156 let ascii = String::from_utf8_lossy(&bytes);
157
158 write!(f, "{}", ascii)
159 }
160}
161
162impl Debug for FourCC {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 let bytes: [u8; 4] = self.code.to_le_bytes();
165
166 let ascii = String::from_utf8_lossy(&bytes);
167
168 write!(f, "FourCC({})", ascii,)
169 }
170}
171
172impl From<FourCCVideo> for FourCC {
173 fn from(value: FourCCVideo) -> Self {
174 FourCC {
175 code: value.to_ffi(),
176 }
177 }
178}
179
180impl From<FourCCAudio> for FourCC {
181 fn from(value: FourCCAudio) -> Self {
182 FourCC {
183 code: value.to_ffi(),
184 }
185 }
186}
187
188impl From<i32> for FourCC {
189 fn from(code: i32) -> Self {
190 FourCC { code }
191 }
192}
193
194impl From<FourCC> for i32 {
195 fn from(fourcc: FourCC) -> Self {
196 fourcc.code
197 }
198}
199
200impl TryFrom<FourCC> for FourCCVideo {
201 type Error = ();
202
203 fn try_from(value: FourCC) -> Result<Self, Self::Error> {
204 FourCCVideo::from_ffi(value.code).ok_or(())
205 }
206}
207
208impl TryFrom<FourCC> for FourCCAudio {
209 type Error = ();
210
211 fn try_from(value: FourCC) -> Result<Self, Self::Error> {
212 FourCCAudio::from_ffi(value.code).ok_or(())
213 }
214}
215
216#[cfg(test)]
217mod test {
218 use super::*;
219
220 #[test]
221 fn test_fmt() {
222 let fourcc = FourCC::from(FourCCVideo::RGBA);
223 assert_eq!(fourcc.to_string(), "RGBA");
224 assert_eq!(format!("{:?}", fourcc), "FourCC(RGBA)");
225 }
226}