ndi_sdk_sys/
four_cc.rs

1//! FourCC (Four Character Code) is a sequence of four bytes used to uniquely identify data formats.
2//!
3//! - <https://docs.ndi.video/all/developing-with-ndi/sdk/frame-types#video-frames-ndilib_video_frame_v2_t>
4//! - <https://en.wikipedia.org/wiki/FourCC>
5
6use 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/// Possible FourCC values for video frames.
17#[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    /// Red, Green, Blue, Alpha (8bit)
30    RGBA = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_RGBA,
31    /// RGBA with ignored alpha channel
32    RGBX = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_RGBX,
33    /// Blue, Green, Red, Alpha (8bit)
34    BGRA = bindings::NDIlib_FourCC_video_type_e_NDIlib_FourCC_video_type_BGRA,
35    /// BGRA with ignored alpha channel
36    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    /// Returns information about the memory layout of the frame data
49    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    /// There is no Layout implementation for this FourCC yet
89    UnsupportedFourCC(FourCCVideo),
90    UnspecifiedFourCC,
91}
92
93/// Possible FourCC values for audio frames.
94#[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    /// Floating point
100    #[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/// Represents a generic FourCC code.
115#[repr(transparent)]
116#[derive(Clone, Copy, Hash, PartialEq, Eq)]
117pub struct FourCC {
118    // use anything with 32bit, using signed reduces the number of casts
119    code: i32,
120}
121
122impl FourCC {
123    /// Creates a new FourCC from an i32 code.
124    pub fn from_ffi(code: i32) -> Self {
125        FourCC { code }
126    }
127
128    /// Converts the FourCC to its i32 representation.
129    pub fn to_ffi(self) -> i32 {
130        self.code
131    }
132
133    /// Attempts to convert the FourCC to a FourCCVideo.
134    pub fn as_video(&self) -> Option<FourCCVideo> {
135        FourCCVideo::from_ffi(self.code)
136    }
137
138    /// Attempts to convert the FourCC to a FourCCAudio.
139    pub fn as_audio(&self) -> Option<FourCCAudio> {
140        FourCCAudio::from_ffi(self.code)
141    }
142
143    /// formats the FourCC as a string.
144    #[allow(clippy::inherent_to_string_shadow_display)] // This is a bit more efficient than Display
145    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}