cros_codecs/
lib.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! This crate provides tools to help decode and encode various video codecs, leveraging the
6//! hardware acceleration available on the target.
7//!
8//! The [codec] module contains tools to parse encoded video streams like H.264 or VP9 and extract
9//! the information useful in order to perform e.g. hardware-accelerated decoding.
10//!
11//! The [backend] module contains common backend code. A backend is a provider of some way to
12//! decode or encode a particular codec, like VAAPI.
13//!
14//! The [decoder] module contains decoders that can turn an encoded video stream into a sequence of
15//! decoded frames using the hardware acceleration available on the host.
16//!
17//! The [encoder] module contains encoder that can turn a picture sequence into a compressed
18//! sequence of decodable encoded packets using the hardware acceleration available on the host.
19//!
20//! The [utils] module contains some useful code that is shared between different parts of this
21//! crate and didn't fit any of the modules above.
22
23pub mod bitstream_utils;
24pub mod codec;
25
26#[cfg(feature = "backend")]
27pub mod backend;
28#[cfg(feature = "backend")]
29pub mod c2_wrapper;
30#[cfg(feature = "backend")]
31pub mod decoder;
32#[cfg(feature = "v4l2")]
33pub mod device;
34#[cfg(feature = "backend")]
35pub mod encoder;
36#[cfg(feature = "backend")]
37pub mod image_processing;
38#[cfg(feature = "backend")]
39pub mod utils;
40#[cfg(feature = "backend")]
41pub mod video_frame;
42
43use std::str::FromStr;
44
45#[cfg(feature = "vaapi")]
46pub use libva;
47#[cfg(feature = "v4l2")]
48pub use v4l2r;
49
50#[derive(Debug, PartialEq, Eq, Copy, Clone)]
51pub enum FrameMemoryType {
52    Managed,
53    Prime,
54    User,
55}
56
57impl FromStr for FrameMemoryType {
58    type Err = &'static str;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        match s {
62            "managed" => Ok(FrameMemoryType::Managed),
63            "prime" => Ok(FrameMemoryType::Prime),
64            "user" => Ok(FrameMemoryType::User),
65            _ => Err("unrecognized memory type. Valid values: managed, prime, user"),
66        }
67    }
68}
69
70/// Rounding modes for `Resolution`
71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
72pub enum ResolutionRoundMode {
73    /// Rounds component-wise to the next even value.
74    Even,
75}
76
77/// A frame resolution in pixels.
78#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
79pub struct Resolution {
80    pub width: u32,
81    pub height: u32,
82}
83
84impl Resolution {
85    /// Whether `self` can contain `other`.
86    pub fn can_contain(&self, other: Self) -> bool {
87        self.width >= other.width && self.height >= other.height
88    }
89
90    /// Rounds `self` according to `rnd_mode`.
91    pub fn round(mut self, rnd_mode: ResolutionRoundMode) -> Self {
92        match rnd_mode {
93            ResolutionRoundMode::Even => {
94                if self.width % 2 != 0 {
95                    self.width += 1;
96                }
97
98                if self.height % 2 != 0 {
99                    self.height += 1;
100                }
101            }
102        }
103
104        self
105    }
106
107    pub fn get_area(&self) -> usize {
108        (self.width as usize) * (self.height as usize)
109    }
110}
111
112impl From<(u32, u32)> for Resolution {
113    fn from(value: (u32, u32)) -> Self {
114        Self { width: value.0, height: value.1 }
115    }
116}
117
118impl From<Resolution> for (u32, u32) {
119    fn from(value: Resolution) -> Self {
120        (value.width, value.height)
121    }
122}
123
124#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
125pub struct Rect {
126    pub x: u32,
127    pub y: u32,
128    pub width: u32,
129    pub height: u32,
130}
131
132impl From<Rect> for Resolution {
133    fn from(value: Rect) -> Self {
134        Self { width: value.width - value.x, height: value.height - value.y }
135    }
136}
137
138impl From<Resolution> for Rect {
139    fn from(value: Resolution) -> Self {
140        Self { x: 0, y: 0, width: value.width, height: value.height }
141    }
142}
143
144impl From<((u32, u32), (u32, u32))> for Rect {
145    fn from(value: ((u32, u32), (u32, u32))) -> Self {
146        Self { x: value.0 .0, y: value.0 .1, width: value.1 .0, height: value.1 .1 }
147    }
148}
149/// Wrapper around u32 when they are meant to be a fourcc.
150///
151/// Provides conversion and display/debug implementations useful when dealing with fourcc codes.
152#[derive(Clone, Copy, Default, PartialEq)]
153pub struct Fourcc(u32);
154
155impl From<u32> for Fourcc {
156    fn from(fourcc: u32) -> Self {
157        Self(fourcc)
158    }
159}
160
161impl From<Fourcc> for u32 {
162    fn from(fourcc: Fourcc) -> Self {
163        fourcc.0
164    }
165}
166
167impl From<&[u8; 4]> for Fourcc {
168    fn from(n: &[u8; 4]) -> Self {
169        Self(n[0] as u32 | (n[1] as u32) << 8 | (n[2] as u32) << 16 | (n[3] as u32) << 24)
170    }
171}
172
173impl From<Fourcc> for [u8; 4] {
174    fn from(n: Fourcc) -> Self {
175        [n.0 as u8, (n.0 >> 8) as u8, (n.0 >> 16) as u8, (n.0 >> 24) as u8]
176    }
177}
178
179impl std::fmt::Display for Fourcc {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        let c: [u8; 4] = (*self).into();
182
183        f.write_fmt(format_args!(
184            "{}{}{}{}",
185            c[0] as char, c[1] as char, c[2] as char, c[3] as char
186        ))
187    }
188}
189
190impl std::fmt::Debug for Fourcc {
191    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192        f.write_fmt(format_args!("0x{:08x} ({})", self.0, self))
193    }
194}
195
196/// Formats that buffers can be mapped into for the CPU to read.
197///
198/// The conventions here largely follow these of libyuv.
199#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
200pub enum DecodedFormat {
201    /// Y, U and V planes, 4:2:0 sampling, 8 bits per sample.
202    I420,
203    /// One Y and one interleaved UV plane, 4:2:0 sampling, 8 bits per sample.
204    NV12,
205    /// Y, U and V planes, 4:2:2 sampling, 8 bits per sample.
206    I422,
207    /// Y, U and V planes, 4:4:4 sampling, 8 bits per sample.
208    I444,
209    /// Y, U and V planes, 4:2:0 sampling, 16 bits per sample, LE. Only the 10 LSBs are used.
210    I010,
211    /// Y, U and V planes, 4:2:0 sampling, 16 bits per sample, LE. Only the 12 LSBs are used.
212    I012,
213    /// Y, U and V planes, 4:2:2 sampling, 16 bits per sample, LE. Only the 10 LSBs are used.
214    I210,
215    /// Y, U and V planes, 4:2:2 sampling, 16 bits per sample, LE. Only the 12 LSBs are used.
216    I212,
217    /// Y, U and V planes, 4:4:4 sampling, 16 bits per sample, LE. Only the 10 LSBs are used.
218    I410,
219    /// Y, U and V planes, 4:4:4 sampling, 16 bits per sample, LE. Only the 12 LSBs are used.
220    I412,
221    /// One Y and one interleaved UV plane, 4:2:0 sampling, 8 bits per sample.
222    /// In a tiled format.
223    MM21,
224}
225
226impl FromStr for DecodedFormat {
227    type Err = &'static str;
228
229    fn from_str(s: &str) -> Result<Self, Self::Err> {
230        match s {
231            "i420" | "I420" => Ok(DecodedFormat::I420),
232            "i422" | "I422" => Ok(DecodedFormat::I422),
233            "i444" | "I444" => Ok(DecodedFormat::I444),
234            "nv12" | "NV12" => Ok(DecodedFormat::NV12),
235            "i010" | "I010" => Ok(DecodedFormat::I010),
236            "i012" | "I012" => Ok(DecodedFormat::I012),
237            "i210" | "I210" => Ok(DecodedFormat::I210),
238            "i212" | "I212" => Ok(DecodedFormat::I212),
239            "i410" | "I410" => Ok(DecodedFormat::I410),
240            "i412" | "I412" => Ok(DecodedFormat::I412),
241            "mm21" | "MM21" => Ok(DecodedFormat::MM21),
242            _ => Err("unrecognized output format. \
243                Valid values: i420, nv12, i422, i444, i010, i012, i210, i212, i410, i412, mm21"),
244        }
245    }
246}
247
248impl From<Fourcc> for DecodedFormat {
249    fn from(fourcc: Fourcc) -> DecodedFormat {
250        match fourcc.to_string().as_str() {
251            "I420" => DecodedFormat::I420,
252            "NV12" | "NM12" => DecodedFormat::NV12,
253            "MM21" => DecodedFormat::MM21,
254            _ => todo!("Fourcc {} not yet supported", fourcc),
255        }
256    }
257}
258
259impl From<DecodedFormat> for Fourcc {
260    fn from(format: DecodedFormat) -> Fourcc {
261        match format {
262            DecodedFormat::I420 => Fourcc::from(b"I420"),
263            DecodedFormat::NV12 => Fourcc::from(b"NV12"),
264            DecodedFormat::MM21 => Fourcc::from(b"MM21"),
265            _ => todo!(),
266        }
267    }
268}
269
270#[derive(Debug, PartialEq, Eq, Copy, Clone)]
271pub enum EncodedFormat {
272    H264,
273    H265,
274    VP8,
275    VP9,
276    AV1,
277}
278
279impl FromStr for EncodedFormat {
280    type Err = &'static str;
281
282    fn from_str(s: &str) -> Result<Self, Self::Err> {
283        match s {
284            "h264" | "H264" => Ok(EncodedFormat::H264),
285            "h265" | "H265" => Ok(EncodedFormat::H265),
286            "vp8" | "VP8" => Ok(EncodedFormat::VP8),
287            "vp9" | "VP9" => Ok(EncodedFormat::VP9),
288            "av1" | "AV1" => Ok(EncodedFormat::AV1),
289            _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"),
290        }
291    }
292}
293
294impl From<Fourcc> for EncodedFormat {
295    fn from(fourcc: Fourcc) -> EncodedFormat {
296        match fourcc.to_string().as_str() {
297            "H264" => EncodedFormat::H264,
298            "HEVC" => EncodedFormat::H265,
299            "VP80" => EncodedFormat::VP8,
300            "VP90" => EncodedFormat::VP9,
301            "AV1F" => EncodedFormat::AV1,
302            _ => todo!("Fourcc {} not yet supported", fourcc),
303        }
304    }
305}
306
307impl From<EncodedFormat> for Fourcc {
308    fn from(format: EncodedFormat) -> Fourcc {
309        match format {
310            EncodedFormat::H264 => Fourcc::from(b"H264"),
311            EncodedFormat::H265 => Fourcc::from(b"HEVC"),
312            EncodedFormat::VP8 => Fourcc::from(b"VP80"),
313            EncodedFormat::VP9 => Fourcc::from(b"VP90"),
314            EncodedFormat::AV1 => Fourcc::from(b"AV1F"),
315        }
316    }
317}
318
319/// Describes the layout of a plane within a frame.
320#[derive(Debug, Default, Clone, PartialEq)]
321pub struct PlaneLayout {
322    /// Index of the memory buffer the plane belongs to.
323    pub buffer_index: usize,
324    /// Start offset of the plane within its buffer.
325    pub offset: usize,
326    /// Distance in bytes between two lines of data in this plane.
327    pub stride: usize,
328}
329
330/// Unambiguously describes the layout of a frame.
331///
332/// A frame can be made of one or several memory buffers, each containing one or several planes.
333/// For a given frame, this structure defines where each plane can be found.
334#[derive(Debug, Default, Clone, PartialEq)]
335pub struct FrameLayout {
336    /// `(Fourcc, modifier)` tuple describing the arrangement of the planes.
337    ///
338    /// This member is enough to infer how many planes and buffers the frame has, and which
339    /// buffer each plane belongs into.
340    pub format: (Fourcc, u64),
341    /// Size in pixels of the frame.
342    pub size: Resolution,
343    /// Layout of each individual plane.
344    pub planes: Vec<PlaneLayout>,
345}
346
347/// Build a frame memory descriptor enum that supports multiple descriptor types.
348///
349/// This is useful for the case where the frames' memory backing is not decided at compile-time.
350/// In this case, this macro can be used to list all the potential types supported at run-time, and
351/// the selected one can be built as the program is run.
352///
353/// # Example
354///
355///
356/// use cros_codecs::multiple_desc_type;
357/// use cros_codecs::utils::DmabufFrame;
358///
359/// /// Frames' memory can be provided either by the backend, or via PRIME DMABUF handles.
360/// multiple_desc_type! {
361///     enum OwnedOrDmaDescriptor {
362///         Owned(()),
363///         Dmabuf(DmabufFrame),
364///     }
365/// }
366///
367#[macro_export]
368macro_rules! multiple_desc_type {
369    (enum $s:ident { $($v:ident($t:ty),)* } ) => {
370        pub enum $s {
371            $($v($t),)*
372        }
373
374        #[cfg(feature = "vaapi")]
375        impl libva::SurfaceMemoryDescriptor for $s {
376            fn add_attrs(&mut self, attrs: &mut Vec<libva::VASurfaceAttrib>) -> Option<Box<dyn std::any::Any>> {
377                match self {
378                    $($s::$v(desc) => desc.add_attrs(attrs),)*
379                }
380            }
381        }
382    }
383}
384
385/// Returns the size required to store a frame of `format` with size `width`x`height`, without any
386/// padding. This is the minimum size of the destination buffer passed to `nv12_copy` or
387/// `i420_copy`.
388pub fn decoded_frame_size(format: DecodedFormat, width: usize, height: usize) -> usize {
389    match format {
390        DecodedFormat::I420 | DecodedFormat::NV12 => {
391            let u_size = width * height;
392            // U and V planes need to be aligned to 2.
393            let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2;
394
395            u_size + uv_size
396        }
397        DecodedFormat::I422 => {
398            let u_size = width * height;
399            // U and V planes need to be aligned to 2.
400            let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2;
401
402            u_size + uv_size
403        }
404        DecodedFormat::I444 => (width * height) * 3,
405        DecodedFormat::I010 | DecodedFormat::I012 => {
406            decoded_frame_size(DecodedFormat::I420, width, height) * 2
407        }
408        DecodedFormat::I210 | DecodedFormat::I212 => {
409            let u_size = width * height * 2;
410            // U and V planes need to be aligned to 2.
411            let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2;
412
413            u_size + uv_size
414        }
415        DecodedFormat::I410 | DecodedFormat::I412 => (width * height * 2) * 3,
416        DecodedFormat::MM21 => panic!("Unable to convert to MM21"),
417    }
418}
419
420/// Instructs on whether it should block on the operation(s).
421#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
422pub enum BlockingMode {
423    Blocking,
424    #[default]
425    NonBlocking,
426}
427
428#[cfg(test)]
429mod tests {
430    use super::Fourcc;
431
432    const NV12_FOURCC: u32 = 0x3231564E;
433
434    #[test]
435    fn fourcc_u32() {
436        let fourcc = Fourcc::from(NV12_FOURCC);
437        let value: u32 = fourcc.into();
438        assert_eq!(value, NV12_FOURCC);
439    }
440
441    #[test]
442    fn fourcc_u8_4() {
443        let fourcc = Fourcc::from(NV12_FOURCC);
444        let value: [u8; 4] = fourcc.into();
445        assert_eq!(value, *b"NV12");
446    }
447
448    #[test]
449    fn fourcc_display() {
450        let fourcc = Fourcc::from(NV12_FOURCC);
451        assert_eq!(fourcc.to_string(), "NV12");
452    }
453
454    #[test]
455    fn fourcc_debug() {
456        let fourcc = Fourcc::from(NV12_FOURCC);
457        assert_eq!(format!("{:?}", fourcc), "0x3231564e (NV12)");
458    }
459}