1pub mod backend;
24pub mod codec;
25pub mod decoder;
26pub mod encoder;
27pub mod utils;
28
29use std::str::FromStr;
30
31use byteorder::ByteOrder;
32use byteorder::LittleEndian;
33#[cfg(feature = "vaapi")]
34pub use libva;
35#[cfg(feature = "v4l2")]
36pub use v4l2r;
37
38#[derive(Copy, Clone, Debug, PartialEq, Eq)]
40pub enum ResolutionRoundMode {
41 Even,
43}
44
45#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
47pub struct Resolution {
48 pub width: u32,
49 pub height: u32,
50}
51
52impl Resolution {
53 pub fn can_contain(&self, other: Self) -> bool {
55 self.width >= other.width && self.height >= other.height
56 }
57
58 pub fn round(mut self, rnd_mode: ResolutionRoundMode) -> Self {
60 match rnd_mode {
61 ResolutionRoundMode::Even => {
62 if self.width % 2 != 0 {
63 self.width += 1;
64 }
65
66 if self.height % 2 != 0 {
67 self.height += 1;
68 }
69 }
70 }
71
72 self
73 }
74}
75
76impl From<(u32, u32)> for Resolution {
77 fn from(value: (u32, u32)) -> Self {
78 Self {
79 width: value.0,
80 height: value.1,
81 }
82 }
83}
84
85impl From<Resolution> for (u32, u32) {
86 fn from(value: Resolution) -> Self {
87 (value.width, value.height)
88 }
89}
90
91#[derive(Clone, Copy, PartialEq)]
95pub struct Fourcc(u32);
96
97impl From<u32> for Fourcc {
98 fn from(fourcc: u32) -> Self {
99 Self(fourcc)
100 }
101}
102
103impl From<Fourcc> for u32 {
104 fn from(fourcc: Fourcc) -> Self {
105 fourcc.0
106 }
107}
108
109impl From<&[u8; 4]> for Fourcc {
110 fn from(n: &[u8; 4]) -> Self {
111 Self(n[0] as u32 | (n[1] as u32) << 8 | (n[2] as u32) << 16 | (n[3] as u32) << 24)
112 }
113}
114
115impl From<Fourcc> for [u8; 4] {
116 fn from(n: Fourcc) -> Self {
117 [
118 n.0 as u8,
119 (n.0 >> 8) as u8,
120 (n.0 >> 16) as u8,
121 (n.0 >> 24) as u8,
122 ]
123 }
124}
125
126impl std::fmt::Display for Fourcc {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 let c: [u8; 4] = (*self).into();
129
130 f.write_fmt(format_args!(
131 "{}{}{}{}",
132 c[0] as char, c[1] as char, c[2] as char, c[3] as char
133 ))
134 }
135}
136
137impl std::fmt::Debug for Fourcc {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 f.write_fmt(format_args!("0x{:08x} ({})", self.0, self))
140 }
141}
142
143#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
147pub enum DecodedFormat {
148 I420,
150 NV12,
152 I422,
154 I444,
156 I010,
158 I012,
160 I210,
162 I212,
164 I410,
166 I412,
168}
169
170impl FromStr for DecodedFormat {
171 type Err = &'static str;
172
173 fn from_str(s: &str) -> Result<Self, Self::Err> {
174 match s {
175 "i420" | "I420" => Ok(DecodedFormat::I420),
176 "i422" | "I422" => Ok(DecodedFormat::I422),
177 "i444" | "I444" => Ok(DecodedFormat::I444),
178 "nv12" | "NV12" => Ok(DecodedFormat::NV12),
179 "i010" | "I010" => Ok(DecodedFormat::I010),
180 "i012" | "I012" => Ok(DecodedFormat::I012),
181 "i210" | "I210" => Ok(DecodedFormat::I210),
182 "i212" | "I212" => Ok(DecodedFormat::I212),
183 "i410" | "I410" => Ok(DecodedFormat::I410),
184 "i412" | "I412" => Ok(DecodedFormat::I412),
185 _ => {
186 Err("unrecognized output format. Valid values: i420, nv12, i422, i444, i010, i012, i210, i212, i410, i412")
187 }
188 }
189 }
190}
191
192#[derive(Debug, Clone, PartialEq)]
194pub struct PlaneLayout {
195 pub buffer_index: usize,
197 pub offset: usize,
199 pub stride: usize,
201}
202
203#[derive(Debug, Clone, PartialEq)]
208pub struct FrameLayout {
209 pub format: (Fourcc, u64),
214 pub size: Resolution,
216 pub planes: Vec<PlaneLayout>,
218}
219
220#[macro_export]
241macro_rules! multiple_desc_type {
242 (enum $s:ident { $($v:ident($t:ty),)* } ) => {
243 enum $s {
244 $($v($t),)*
245 }
246
247 #[cfg(feature = "vaapi")]
248 impl libva::SurfaceMemoryDescriptor for $s {
249 fn add_attrs(&mut self, attrs: &mut Vec<libva::VASurfaceAttrib>) -> Option<Box<dyn std::any::Any>> {
250 match self {
251 $($s::$v(desc) => desc.add_attrs(attrs),)*
252 }
253 }
254 }
255 }
256}
257
258pub fn nv12_copy(
260 src: &[u8],
261 dst: &mut [u8],
262 width: usize,
263 height: usize,
264 strides: [usize; 3],
265 offsets: [usize; 3],
266) {
267 let src_y_lines = src[offsets[0]..]
269 .chunks(strides[0])
270 .map(|line| &line[..width]);
271 let dst_y_lines = dst.chunks_mut(width);
272
273 for (src_line, dst_line) in src_y_lines.zip(dst_y_lines).take(height) {
274 dst_line.copy_from_slice(src_line);
275 }
276
277 let dst_u_offset = width * height;
278
279 let uv_width = if width % 2 == 1 { width + 1 } else { width };
282 let uv_height = if height % 2 == 1 { height + 1 } else { height } / 2;
283
284 let src_uv_lines = src[offsets[1]..]
286 .chunks(strides[1])
287 .map(|line| &line[..uv_width]);
288 let dst_uv_lines = dst[dst_u_offset..].chunks_mut(uv_width);
289 for (src_line, dst_line) in src_uv_lines.zip(dst_uv_lines).take(uv_height) {
290 dst_line.copy_from_slice(src_line);
291 }
292}
293
294pub fn i4xx_copy(
306 src: &[u8],
307 dst: &mut [u8],
308 width: usize,
309 height: usize,
310 strides: [usize; 3],
311 offsets: [usize; 3],
312 (sub_h, sub_v): (bool, bool),
313) {
314 let uv_width = if sub_h { (width + 1) / 2 } else { width };
316 let uv_height = if sub_v { (height + 1) / 2 } else { height };
317
318 let dst_y_size = width * height;
319 let dst_u_size = uv_width * uv_height;
320 let (dst_y_plane, dst_uv_planes) = dst.split_at_mut(dst_y_size);
321 let (dst_u_plane, dst_v_plane) = dst_uv_planes.split_at_mut(dst_u_size);
322
323 let src_y_lines = src[offsets[0]..]
325 .chunks(strides[0])
326 .map(|line| &line[..width]);
327 let dst_y_lines = dst_y_plane.chunks_mut(width);
328 for (src_line, dst_line) in src_y_lines.zip(dst_y_lines).take(height) {
329 dst_line.copy_from_slice(src_line);
330 }
331
332 let src_u_lines = src[offsets[1]..]
334 .chunks(strides[1])
335 .map(|line| &line[..uv_width]);
336 let dst_u_lines = dst_u_plane.chunks_mut(uv_width);
337 for (src_line, dst_line) in src_u_lines.zip(dst_u_lines).take(uv_height) {
338 dst_line.copy_from_slice(src_line);
339 }
340
341 let src_v_lines = src[offsets[2]..]
343 .chunks(strides[2])
344 .map(|line| &line[..uv_width]);
345 let dst_v_lines = dst_v_plane.chunks_mut(uv_width);
346 for (src_line, dst_line) in src_v_lines.zip(dst_v_lines).take(uv_height) {
347 dst_line.copy_from_slice(src_line);
348 }
349}
350
351pub fn decoded_frame_size(format: DecodedFormat, width: usize, height: usize) -> usize {
355 match format {
356 DecodedFormat::I420 | DecodedFormat::NV12 => {
357 let u_size = width * height;
358 let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2;
360
361 u_size + uv_size
362 }
363 DecodedFormat::I422 => {
364 let u_size = width * height;
365 let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2;
367
368 u_size + uv_size
369 }
370 DecodedFormat::I444 => (width * height) * 3,
371 DecodedFormat::I010 | DecodedFormat::I012 => {
372 decoded_frame_size(DecodedFormat::I420, width, height) * 2
373 }
374 DecodedFormat::I210 | DecodedFormat::I212 => {
375 let u_size = width * height * 2;
376 let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2;
378
379 u_size + uv_size
380 }
381 DecodedFormat::I410 | DecodedFormat::I412 => (width * height * 2) * 3,
382 }
383}
384
385fn y410_to_i410(
388 src: &[u8],
389 dst: &mut [u8],
390 width: usize,
391 height: usize,
392 strides: [usize; 3],
393 offsets: [usize; 3],
394) {
395 let src_lines = src[offsets[0]..]
396 .chunks(strides[0])
397 .map(|line| &line[..width * 4]);
398
399 let dst_y_size = width * 2 * height;
400 let dst_u_size = width * 2 * height;
401
402 let (dst_y_plane, dst_uv_planes) = dst.split_at_mut(dst_y_size);
403 let (dst_u_plane, dst_v_plane) = dst_uv_planes.split_at_mut(dst_u_size);
404 let dst_y_lines = dst_y_plane.chunks_mut(width * 2);
405 let dst_u_lines = dst_u_plane.chunks_mut(width * 2);
406 let dst_v_lines = dst_v_plane.chunks_mut(width * 2);
407
408 for (src_line, (dst_y_line, (dst_u_line, dst_v_line))) in src_lines
409 .zip(dst_y_lines.zip(dst_u_lines.zip(dst_v_lines)))
410 .take(height)
411 {
412 for (src, (dst_y, (dst_u, dst_v))) in src_line.chunks(4).zip(
413 dst_y_line
414 .chunks_mut(2)
415 .zip(dst_u_line.chunks_mut(2).zip(dst_v_line.chunks_mut(2))),
416 ) {
417 let y = LittleEndian::read_u16(&[src[1] >> 2 | src[2] << 6, src[2] >> 2 & 0b11]);
418 let u = LittleEndian::read_u16(&[src[0], src[1] & 0b11]);
419 let v = LittleEndian::read_u16(&[src[2] >> 4 | src[3] << 4, src[3] >> 4 & 0b11]);
420 LittleEndian::write_u16(dst_y, y);
421 LittleEndian::write_u16(dst_u, u);
422 LittleEndian::write_u16(dst_v, v);
423 }
424 }
425}
426
427#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
429pub enum BlockingMode {
430 Blocking,
431 #[default]
432 NonBlocking,
433}
434
435#[cfg(test)]
436mod tests {
437 use super::Fourcc;
438
439 const NV12_FOURCC: u32 = 0x3231564E;
440
441 #[test]
442 fn fourcc_u32() {
443 let fourcc = Fourcc::from(NV12_FOURCC);
444 let value: u32 = fourcc.into();
445 assert_eq!(value, NV12_FOURCC);
446 }
447
448 #[test]
449 fn fourcc_u8_4() {
450 let fourcc = Fourcc::from(NV12_FOURCC);
451 let value: [u8; 4] = fourcc.into();
452 assert_eq!(value, *b"NV12");
453 }
454
455 #[test]
456 fn fourcc_display() {
457 let fourcc = Fourcc::from(NV12_FOURCC);
458 assert_eq!(fourcc.to_string(), "NV12");
459 }
460
461 #[test]
462 fn fourcc_debug() {
463 let fourcc = Fourcc::from(NV12_FOURCC);
464 assert_eq!(format!("{:?}", fourcc), "0x3231564e (NV12)");
465 }
466}