// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! This crate provides tools to help decode and encode various video codecs, leveraging the //! hardware acceleration available on the target. //! //! The [codec] module contains tools to parse encoded video streams like H.264 or VP9 and extract //! the information useful in order to perform e.g. hardware-accelerated decoding. //! //! The [backend] module contains common backend code. A backend is a provider of some way to //! decode or encode a particular codec, like VAAPI. //! //! The [decoder] module contains decoders that can turn an encoded video stream into a sequence of //! decoded frames using the hardware acceleration available on the host. //! //! The [encoder] module contains encoder that can turn a picture sequence into a compressed //! sequence of decodable encoded packets using the hardware acceleration available on the host. //! //! The [utils] module contains some useful code that is shared between different parts of this //! crate and didn't fit any of the modules above. pub mod bitstream_utils; pub mod codec; #[cfg(feature = "backend")] pub mod backend; #[cfg(feature = "backend")] pub mod c2_wrapper; #[cfg(feature = "backend")] pub mod decoder; #[cfg(feature = "v4l2")] pub mod device; #[cfg(feature = "backend")] pub mod encoder; #[cfg(feature = "backend")] pub mod image_processing; #[cfg(feature = "backend")] pub mod utils; #[cfg(feature = "backend")] pub mod video_frame; use std::str::FromStr; #[cfg(feature = "vaapi")] pub use libva; #[cfg(feature = "v4l2")] pub use v4l2r; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FrameMemoryType { Managed, Prime, User, } impl FromStr for FrameMemoryType { type Err = &'static str; fn from_str(s: &str) -> Result { match s { "managed" => Ok(FrameMemoryType::Managed), "prime" => Ok(FrameMemoryType::Prime), "user" => Ok(FrameMemoryType::User), _ => Err("unrecognized memory type. Valid values: managed, prime, user"), } } } /// Rounding modes for `Resolution` #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ResolutionRoundMode { /// Rounds component-wise to the next even value. Even, } /// A frame resolution in pixels. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Resolution { pub width: u32, pub height: u32, } impl Resolution { /// Whether `self` can contain `other`. pub fn can_contain(&self, other: Self) -> bool { self.width >= other.width && self.height >= other.height } /// Rounds `self` according to `rnd_mode`. pub fn round(mut self, rnd_mode: ResolutionRoundMode) -> Self { match rnd_mode { ResolutionRoundMode::Even => { if self.width % 2 != 0 { self.width += 1; } if self.height % 2 != 0 { self.height += 1; } } } self } pub fn get_area(&self) -> usize { (self.width as usize) * (self.height as usize) } } impl From<(u32, u32)> for Resolution { fn from(value: (u32, u32)) -> Self { Self { width: value.0, height: value.1 } } } impl From for (u32, u32) { fn from(value: Resolution) -> Self { (value.width, value.height) } } #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct Rect { pub x: u32, pub y: u32, pub width: u32, pub height: u32, } impl From for Resolution { fn from(value: Rect) -> Self { Self { width: value.width - value.x, height: value.height - value.y } } } impl From for Rect { fn from(value: Resolution) -> Self { Self { x: 0, y: 0, width: value.width, height: value.height } } } impl From<((u32, u32), (u32, u32))> for Rect { fn from(value: ((u32, u32), (u32, u32))) -> Self { Self { x: value.0 .0, y: value.0 .1, width: value.1 .0, height: value.1 .1 } } } /// Wrapper around u32 when they are meant to be a fourcc. /// /// Provides conversion and display/debug implementations useful when dealing with fourcc codes. #[derive(Clone, Copy, Default, PartialEq)] pub struct Fourcc(u32); impl From for Fourcc { fn from(fourcc: u32) -> Self { Self(fourcc) } } impl From for u32 { fn from(fourcc: Fourcc) -> Self { fourcc.0 } } impl From<&[u8; 4]> for Fourcc { fn from(n: &[u8; 4]) -> Self { Self(n[0] as u32 | (n[1] as u32) << 8 | (n[2] as u32) << 16 | (n[3] as u32) << 24) } } impl From for [u8; 4] { fn from(n: Fourcc) -> Self { [n.0 as u8, (n.0 >> 8) as u8, (n.0 >> 16) as u8, (n.0 >> 24) as u8] } } impl std::fmt::Display for Fourcc { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c: [u8; 4] = (*self).into(); f.write_fmt(format_args!( "{}{}{}{}", c[0] as char, c[1] as char, c[2] as char, c[3] as char )) } } impl std::fmt::Debug for Fourcc { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("0x{:08x} ({})", self.0, self)) } } /// Formats that buffers can be mapped into for the CPU to read. /// /// The conventions here largely follow these of libyuv. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum DecodedFormat { /// Y, U and V planes, 4:2:0 sampling, 8 bits per sample. I420, /// One Y and one interleaved UV plane, 4:2:0 sampling, 8 bits per sample. NV12, /// Y, U and V planes, 4:2:2 sampling, 8 bits per sample. I422, /// Y, U and V planes, 4:4:4 sampling, 8 bits per sample. I444, /// Y, U and V planes, 4:2:0 sampling, 16 bits per sample, LE. Only the 10 LSBs are used. I010, /// Y, U and V planes, 4:2:0 sampling, 16 bits per sample, LE. Only the 12 LSBs are used. I012, /// Y, U and V planes, 4:2:2 sampling, 16 bits per sample, LE. Only the 10 LSBs are used. I210, /// Y, U and V planes, 4:2:2 sampling, 16 bits per sample, LE. Only the 12 LSBs are used. I212, /// Y, U and V planes, 4:4:4 sampling, 16 bits per sample, LE. Only the 10 LSBs are used. I410, /// Y, U and V planes, 4:4:4 sampling, 16 bits per sample, LE. Only the 12 LSBs are used. I412, /// One Y and one interleaved UV plane, 4:2:0 sampling, 8 bits per sample. /// In a tiled format. MM21, } impl FromStr for DecodedFormat { type Err = &'static str; fn from_str(s: &str) -> Result { match s { "i420" | "I420" => Ok(DecodedFormat::I420), "i422" | "I422" => Ok(DecodedFormat::I422), "i444" | "I444" => Ok(DecodedFormat::I444), "nv12" | "NV12" => Ok(DecodedFormat::NV12), "i010" | "I010" => Ok(DecodedFormat::I010), "i012" | "I012" => Ok(DecodedFormat::I012), "i210" | "I210" => Ok(DecodedFormat::I210), "i212" | "I212" => Ok(DecodedFormat::I212), "i410" | "I410" => Ok(DecodedFormat::I410), "i412" | "I412" => Ok(DecodedFormat::I412), "mm21" | "MM21" => Ok(DecodedFormat::MM21), _ => Err("unrecognized output format. \ Valid values: i420, nv12, i422, i444, i010, i012, i210, i212, i410, i412, mm21"), } } } impl From for DecodedFormat { fn from(fourcc: Fourcc) -> DecodedFormat { match fourcc.to_string().as_str() { "I420" => DecodedFormat::I420, "NV12" | "NM12" => DecodedFormat::NV12, "MM21" => DecodedFormat::MM21, _ => todo!("Fourcc {} not yet supported", fourcc), } } } impl From for Fourcc { fn from(format: DecodedFormat) -> Fourcc { match format { DecodedFormat::I420 => Fourcc::from(b"I420"), DecodedFormat::NV12 => Fourcc::from(b"NV12"), DecodedFormat::MM21 => Fourcc::from(b"MM21"), _ => todo!(), } } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum EncodedFormat { H264, H265, VP8, VP9, AV1, } impl FromStr for EncodedFormat { type Err = &'static str; fn from_str(s: &str) -> Result { match s { "h264" | "H264" => Ok(EncodedFormat::H264), "h265" | "H265" => Ok(EncodedFormat::H265), "vp8" | "VP8" => Ok(EncodedFormat::VP8), "vp9" | "VP9" => Ok(EncodedFormat::VP9), "av1" | "AV1" => Ok(EncodedFormat::AV1), _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"), } } } impl From for EncodedFormat { fn from(fourcc: Fourcc) -> EncodedFormat { match fourcc.to_string().as_str() { "H264" => EncodedFormat::H264, "HEVC" => EncodedFormat::H265, "VP80" => EncodedFormat::VP8, "VP90" => EncodedFormat::VP9, "AV1F" => EncodedFormat::AV1, _ => todo!("Fourcc {} not yet supported", fourcc), } } } impl From for Fourcc { fn from(format: EncodedFormat) -> Fourcc { match format { EncodedFormat::H264 => Fourcc::from(b"H264"), EncodedFormat::H265 => Fourcc::from(b"HEVC"), EncodedFormat::VP8 => Fourcc::from(b"VP80"), EncodedFormat::VP9 => Fourcc::from(b"VP90"), EncodedFormat::AV1 => Fourcc::from(b"AV1F"), } } } /// Describes the layout of a plane within a frame. #[derive(Debug, Default, Clone, PartialEq)] pub struct PlaneLayout { /// Index of the memory buffer the plane belongs to. pub buffer_index: usize, /// Start offset of the plane within its buffer. pub offset: usize, /// Distance in bytes between two lines of data in this plane. pub stride: usize, } /// Unambiguously describes the layout of a frame. /// /// A frame can be made of one or several memory buffers, each containing one or several planes. /// For a given frame, this structure defines where each plane can be found. #[derive(Debug, Default, Clone, PartialEq)] pub struct FrameLayout { /// `(Fourcc, modifier)` tuple describing the arrangement of the planes. /// /// This member is enough to infer how many planes and buffers the frame has, and which /// buffer each plane belongs into. pub format: (Fourcc, u64), /// Size in pixels of the frame. pub size: Resolution, /// Layout of each individual plane. pub planes: Vec, } /// Build a frame memory descriptor enum that supports multiple descriptor types. /// /// This is useful for the case where the frames' memory backing is not decided at compile-time. /// In this case, this macro can be used to list all the potential types supported at run-time, and /// the selected one can be built as the program is run. /// /// # Example /// /// /// use cros_codecs::multiple_desc_type; /// use cros_codecs::utils::DmabufFrame; /// /// /// Frames' memory can be provided either by the backend, or via PRIME DMABUF handles. /// multiple_desc_type! { /// enum OwnedOrDmaDescriptor { /// Owned(()), /// Dmabuf(DmabufFrame), /// } /// } /// #[macro_export] macro_rules! multiple_desc_type { (enum $s:ident { $($v:ident($t:ty),)* } ) => { pub enum $s { $($v($t),)* } #[cfg(feature = "vaapi")] impl libva::SurfaceMemoryDescriptor for $s { fn add_attrs(&mut self, attrs: &mut Vec) -> Option> { match self { $($s::$v(desc) => desc.add_attrs(attrs),)* } } } } } /// Returns the size required to store a frame of `format` with size `width`x`height`, without any /// padding. This is the minimum size of the destination buffer passed to `nv12_copy` or /// `i420_copy`. pub fn decoded_frame_size(format: DecodedFormat, width: usize, height: usize) -> usize { match format { DecodedFormat::I420 | DecodedFormat::NV12 => { let u_size = width * height; // U and V planes need to be aligned to 2. let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2; u_size + uv_size } DecodedFormat::I422 => { let u_size = width * height; // U and V planes need to be aligned to 2. let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2; u_size + uv_size } DecodedFormat::I444 => (width * height) * 3, DecodedFormat::I010 | DecodedFormat::I012 => { decoded_frame_size(DecodedFormat::I420, width, height) * 2 } DecodedFormat::I210 | DecodedFormat::I212 => { let u_size = width * height * 2; // U and V planes need to be aligned to 2. let uv_size = ((width + 1) / 2) * ((height + 1) / 2) * 2 * 2; u_size + uv_size } DecodedFormat::I410 | DecodedFormat::I412 => (width * height * 2) * 3, DecodedFormat::MM21 => panic!("Unable to convert to MM21"), } } /// Instructs on whether it should block on the operation(s). #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub enum BlockingMode { Blocking, #[default] NonBlocking, } #[cfg(test)] mod tests { use super::Fourcc; const NV12_FOURCC: u32 = 0x3231564E; #[test] fn fourcc_u32() { let fourcc = Fourcc::from(NV12_FOURCC); let value: u32 = fourcc.into(); assert_eq!(value, NV12_FOURCC); } #[test] fn fourcc_u8_4() { let fourcc = Fourcc::from(NV12_FOURCC); let value: [u8; 4] = fourcc.into(); assert_eq!(value, *b"NV12"); } #[test] fn fourcc_display() { let fourcc = Fourcc::from(NV12_FOURCC); assert_eq!(fourcc.to_string(), "NV12"); } #[test] fn fourcc_debug() { let fourcc = Fourcc::from(NV12_FOURCC); assert_eq!(format!("{:?}", fourcc), "0x3231564e (NV12)"); } }