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
23 pub mod bitstream_utils;
24 pub mod codec;
25
26 #[cfg(feature = "backend")]
27 pub mod backend;
28 #[cfg(feature = "backend")]
29 pub mod c2_wrapper;
30 #[cfg(feature = "backend")]
31 pub mod decoder;
32 #[cfg(feature = "v4l2")]
33 pub mod device;
34 #[cfg(feature = "backend")]
35 pub mod encoder;
36 #[cfg(feature = "backend")]
37 pub mod image_processing;
38 #[cfg(feature = "backend")]
39 pub mod utils;
40 #[cfg(feature = "backend")]
41 pub mod video_frame;
42
43 use std::str::FromStr;
44
45 #[cfg(feature = "vaapi")]
46 pub use libva;
47 #[cfg(feature = "v4l2")]
48 pub use v4l2r;
49
50 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
51 pub enum FrameMemoryType {
52 Managed,
53 Prime,
54 User,
55 }
56
57 impl FromStr for FrameMemoryType {
58 type Err = &'static str;
59
from_str(s: &str) -> Result<Self, Self::Err>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)]
72 pub 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)]
79 pub struct Resolution {
80 pub width: u32,
81 pub height: u32,
82 }
83
84 impl Resolution {
85 /// Whether `self` can contain `other`.
can_contain(&self, other: Self) -> bool86 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`.
round(mut self, rnd_mode: ResolutionRoundMode) -> Self91 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
get_area(&self) -> usize107 pub fn get_area(&self) -> usize {
108 (self.width as usize) * (self.height as usize)
109 }
110 }
111
112 impl From<(u32, u32)> for Resolution {
from(value: (u32, u32)) -> Self113 fn from(value: (u32, u32)) -> Self {
114 Self { width: value.0, height: value.1 }
115 }
116 }
117
118 impl From<Resolution> for (u32, u32) {
from(value: Resolution) -> Self119 fn from(value: Resolution) -> Self {
120 (value.width, value.height)
121 }
122 }
123
124 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
125 pub struct Rect {
126 pub x: u32,
127 pub y: u32,
128 pub width: u32,
129 pub height: u32,
130 }
131
132 impl From<Rect> for Resolution {
from(value: Rect) -> Self133 fn from(value: Rect) -> Self {
134 Self { width: value.width - value.x, height: value.height - value.y }
135 }
136 }
137
138 impl From<Resolution> for Rect {
from(value: Resolution) -> Self139 fn from(value: Resolution) -> Self {
140 Self { x: 0, y: 0, width: value.width, height: value.height }
141 }
142 }
143
144 impl From<((u32, u32), (u32, u32))> for Rect {
from(value: ((u32, u32), (u32, u32))) -> Self145 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)]
153 pub struct Fourcc(u32);
154
155 impl From<u32> for Fourcc {
from(fourcc: u32) -> Self156 fn from(fourcc: u32) -> Self {
157 Self(fourcc)
158 }
159 }
160
161 impl From<Fourcc> for u32 {
from(fourcc: Fourcc) -> Self162 fn from(fourcc: Fourcc) -> Self {
163 fourcc.0
164 }
165 }
166
167 impl From<&[u8; 4]> for Fourcc {
from(n: &[u8; 4]) -> Self168 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
173 impl From<Fourcc> for [u8; 4] {
from(n: Fourcc) -> Self174 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
179 impl std::fmt::Display for Fourcc {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result180 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
190 impl std::fmt::Debug for Fourcc {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result191 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)]
200 pub 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
226 impl FromStr for DecodedFormat {
227 type Err = &'static str;
228
from_str(s: &str) -> Result<Self, Self::Err>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
248 impl From<Fourcc> for DecodedFormat {
from(fourcc: Fourcc) -> DecodedFormat249 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
259 impl From<DecodedFormat> for Fourcc {
from(format: DecodedFormat) -> Fourcc260 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)]
271 pub enum EncodedFormat {
272 H264,
273 H265,
274 VP8,
275 VP9,
276 AV1,
277 }
278
279 impl FromStr for EncodedFormat {
280 type Err = &'static str;
281
from_str(s: &str) -> Result<Self, Self::Err>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
294 impl From<Fourcc> for EncodedFormat {
from(fourcc: Fourcc) -> EncodedFormat295 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
307 impl From<EncodedFormat> for Fourcc {
from(format: EncodedFormat) -> Fourcc308 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)]
321 pub 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)]
335 pub 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]
368 macro_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`.
decoded_frame_size(format: DecodedFormat, width: usize, height: usize) -> usize388 pub 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)]
422 pub enum BlockingMode {
423 Blocking,
424 #[default]
425 NonBlocking,
426 }
427
428 #[cfg(test)]
429 mod tests {
430 use super::Fourcc;
431
432 const NV12_FOURCC: u32 = 0x3231564E;
433
434 #[test]
fourcc_u32()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]
fourcc_u8_4()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]
fourcc_display()449 fn fourcc_display() {
450 let fourcc = Fourcc::from(NV12_FOURCC);
451 assert_eq!(fourcc.to_string(), "NV12");
452 }
453
454 #[test]
fourcc_debug()455 fn fourcc_debug() {
456 let fourcc = Fourcc::from(NV12_FOURCC);
457 assert_eq!(format!("{:?}", fourcc), "0x3231564e (NV12)");
458 }
459 }
460