oximedia_core/
sample_format.rs1#![allow(dead_code)]
7#![allow(clippy::cast_precision_loss)]
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum VideoPixelFormat {
12 Yuv420p,
14 Yuv422p,
16 Yuv444p,
18 Rgba,
20 Rgb24,
22 Nv12,
24 P010,
26}
27
28impl VideoPixelFormat {
29 #[must_use]
33 pub fn bits_per_pixel(&self) -> u8 {
34 match self {
35 Self::Yuv420p | Self::Nv12 => 12,
36 Self::Yuv422p => 16,
37 Self::Yuv444p | Self::Rgb24 => 24,
38 Self::Rgba => 32,
39 Self::P010 => 15, }
41 }
42
43 #[must_use]
45 pub fn is_planar(&self) -> bool {
46 matches!(
47 self,
48 Self::Yuv420p | Self::Yuv422p | Self::Yuv444p | Self::Nv12 | Self::P010
49 )
50 }
51
52 #[must_use]
57 pub fn chroma_subsampling(&self) -> (u8, u8) {
58 match self {
59 Self::Yuv420p | Self::Nv12 | Self::P010 => (2, 2),
60 Self::Yuv422p => (2, 1),
61 Self::Yuv444p | Self::Rgba | Self::Rgb24 => (1, 1),
62 }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum AudioSampleFormat {
69 S16,
71 S32,
73 F32,
75 F64,
77 S16P,
79 S32P,
81 F32P,
83}
84
85impl AudioSampleFormat {
86 #[must_use]
88 pub fn bytes_per_sample(&self) -> u8 {
89 match self {
90 Self::S16 | Self::S16P => 2,
91 Self::S32 | Self::F32 | Self::S32P | Self::F32P => 4,
92 Self::F64 => 8,
93 }
94 }
95
96 #[must_use]
98 pub fn is_planar(&self) -> bool {
99 matches!(self, Self::S16P | Self::S32P | Self::F32P)
100 }
101
102 #[must_use]
104 pub fn is_float(&self) -> bool {
105 matches!(self, Self::F32 | Self::F64 | Self::F32P)
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct SampleFormatInfo {
112 pub video: Option<VideoPixelFormat>,
114 pub audio: Option<AudioSampleFormat>,
116}
117
118impl SampleFormatInfo {
119 #[must_use]
121 pub fn new(video: Option<VideoPixelFormat>, audio: Option<AudioSampleFormat>) -> Self {
122 Self { video, audio }
123 }
124
125 #[must_use]
127 pub fn has_video(&self) -> bool {
128 self.video.is_some()
129 }
130
131 #[must_use]
133 pub fn has_audio(&self) -> bool {
134 self.audio.is_some()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
145 fn test_yuv420p_bits_per_pixel() {
146 assert_eq!(VideoPixelFormat::Yuv420p.bits_per_pixel(), 12);
147 }
148
149 #[test]
150 fn test_rgba_bits_per_pixel() {
151 assert_eq!(VideoPixelFormat::Rgba.bits_per_pixel(), 32);
152 }
153
154 #[test]
155 fn test_rgb24_bits_per_pixel() {
156 assert_eq!(VideoPixelFormat::Rgb24.bits_per_pixel(), 24);
157 }
158
159 #[test]
160 fn test_yuv422p_bits_per_pixel() {
161 assert_eq!(VideoPixelFormat::Yuv422p.bits_per_pixel(), 16);
162 }
163
164 #[test]
165 fn test_nv12_is_planar() {
166 assert!(VideoPixelFormat::Nv12.is_planar());
167 }
168
169 #[test]
170 fn test_rgba_is_not_planar() {
171 assert!(!VideoPixelFormat::Rgba.is_planar());
172 }
173
174 #[test]
175 fn test_yuv420p_chroma_subsampling() {
176 assert_eq!(VideoPixelFormat::Yuv420p.chroma_subsampling(), (2, 2));
177 }
178
179 #[test]
180 fn test_yuv422p_chroma_subsampling() {
181 assert_eq!(VideoPixelFormat::Yuv422p.chroma_subsampling(), (2, 1));
182 }
183
184 #[test]
185 fn test_yuv444p_chroma_subsampling() {
186 assert_eq!(VideoPixelFormat::Yuv444p.chroma_subsampling(), (1, 1));
187 }
188
189 #[test]
190 fn test_p010_bits_and_subsampling() {
191 assert_eq!(VideoPixelFormat::P010.bits_per_pixel(), 15);
192 assert_eq!(VideoPixelFormat::P010.chroma_subsampling(), (2, 2));
193 }
194
195 #[test]
198 fn test_s16_bytes_per_sample() {
199 assert_eq!(AudioSampleFormat::S16.bytes_per_sample(), 2);
200 }
201
202 #[test]
203 fn test_f64_bytes_per_sample() {
204 assert_eq!(AudioSampleFormat::F64.bytes_per_sample(), 8);
205 }
206
207 #[test]
208 fn test_f32p_is_planar_and_float() {
209 assert!(AudioSampleFormat::F32P.is_planar());
210 assert!(AudioSampleFormat::F32P.is_float());
211 }
212
213 #[test]
214 fn test_s32_is_not_planar_not_float() {
215 assert!(!AudioSampleFormat::S32.is_planar());
216 assert!(!AudioSampleFormat::S32.is_float());
217 }
218
219 #[test]
222 fn test_sample_format_info_has_video() {
223 let info = SampleFormatInfo::new(Some(VideoPixelFormat::Yuv420p), None);
224 assert!(info.has_video());
225 assert!(!info.has_audio());
226 }
227
228 #[test]
229 fn test_sample_format_info_has_audio() {
230 let info = SampleFormatInfo::new(None, Some(AudioSampleFormat::F32));
231 assert!(!info.has_video());
232 assert!(info.has_audio());
233 }
234
235 #[test]
236 fn test_sample_format_info_both() {
237 let info =
238 SampleFormatInfo::new(Some(VideoPixelFormat::Nv12), Some(AudioSampleFormat::S16P));
239 assert!(info.has_video());
240 assert!(info.has_audio());
241 }
242
243 #[test]
244 fn test_sample_format_info_neither() {
245 let info = SampleFormatInfo::new(None, None);
246 assert!(!info.has_video());
247 assert!(!info.has_audio());
248 }
249}